Learnsymfony 2
Learnsymfony 2
Jon Torrado
This book is for sale at http://leanpub.com/learnsymfony2
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Introduction . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   1
    Why I’m writing this book .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   1
    What you are going to learn .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   2
    Whom is this book for . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   3
    How the book is divided . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   3
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Chapter 5: MopaBootstrap . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29
   Installing MopaBootstrapBundle               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29
   Preparing the base template . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   31
   Tip 5 . . . . . . . . . . . . . . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   36
   Summary . . . . . . . . . . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   37
    Tip 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                        82
    Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                                          83
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Epilogue . . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   148
    Bower . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   148
    Gassetic . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   148
    Gulp with JavaScript . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   148
    PostCSS . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   149
    Symfony made developments          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   149
    DDD . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   149
    Did you like the book? . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   149
    Thanks to readers . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   150
Introduction
Since I started my professional career, almost everything I’ve done was related to the website
world. When I finished my university studies, my knowledgement about web development was very
limited. I just knew a few HTML elements and something a bit more sophisticated called Struts, a
MVC Java framework. I had to learn in a record time in order to live up the environment I was
entering, and that was possible because of the huge documentation and help that everyone can find
through Internet.
I started using a bunch of CMS for different clients, such as Moodle or Drupal, to finally involve
myself in the custom development. Everything was handmade! I had to maintain a website with no
frameworks and almost no libraries at all, everything done from scratch. Thank God I had to do this
kind of stuff! This was the project that totally changed my professional career because I could get
myself inmersed into the deepness of all kind of libraries and spaguetti codes that belonged to really
interesting people.
After serveral unexpected changes, I started my new journey in the Symfony world. Thanks to its
excelent documentation, which I strongly recommend reading more than once a year, and to third
party bundles that are maintained by a really active community, I was developing websites in a very
short term that where incredibly powerful and with a ridiculous effort comparing to what I had been
developing until that moment.
I’ve been developing websites with Symfony for some years, so I consider this the exact moment
to write the book that you are reading right now. Here I’m going to collect what I consider really
important in order to start developing a Symfony project, and I will try to make you able to use
technologies which will make your work easier in a really big way, a way that you would never have
imagined (at least I hadn’t imagined it). Don’t you think that I’m writing the definitive book that
will dominate the world, because nowadays I’m still learning from a sector that changes every single
day and where it’s almost impossible to follow the frenetic changing rhythm where it belongs. But…
I will try to be at the height of the situation. I just want you to do something right before reading
this book: read the Symfony documentation. This documentation is simply great and translated to
a lot of languages, so I won’t repeat any single part of it just because there you can find it perfectly
explained. You don’t have to be a Symfony professional tho, but you have to know what are routes,
controllers, templates and the Symfony console at least (in summary, the first 8 chapters of the
documentation). Are you ready to start? So do I, let’s start!
that I consider vital with any topic, not just this one. I believe that in the actual society, or at least
in the country where I live, we are lack of excelent teachers that try to improve themselves every
day because, making the best of them, they will make their studendts give the best of them too.
I want to connect what I just told you with two very interesting Japanese statements. The first one
(although it’s an urban leyend, let me be happy telling it to you) is that, in their culture, teachers
are the only ones that are allowed to stay without making a reverence to the emperor, because they
consider that without teachers, there will be no emperor.
On the other side there is an idea that since I read it, it has applied a substancial change to my life:
Kaizen. This Japanese idea consists of the improvement process that we should apply every single
day.
What I’m going to write in this book is what I learnt freely during several weeks, even years, and
I’ve decided to share it with you so you don’t have to spend so much time in learning it by yourself.
Moreover, I consider that labeling this book with a price for something I learnt without speding a
single coin and that nowadays makes me eat every month is totally absurd. Because of this, I want
to share the book completely free, but if you like what you have read right after finishing it and
you want to thank me the work I’ve done, you can buy it for the price you consider knowing that
you are contributing for a good cause, because I will donate the 50% of the earnings of this book to
the watchi¹ crowdfunding platform which funds healthcare for people around the world. You learn,
you enjoy and you help a good cause.
    Symfony installer
    There’s been a while since Symfony created its own installer in a .phar³ file. Before having this
    installer, the developers had to download a compressed file from the official Symfony website and
    uncompress it in the folder where he/she was going to develop; after that, they added the project
    to Packagist⁴ and the dependency manager Composer⁵ was used as an installer. Nowadays, the best
    thing you can do is keeping the Symfony installer as a binary of your operative system so you can
    execute it as any other command. In order to achieve this, type the next commands or the ones that
    are in the documentation if you are a not a *NIX user:
    Once this is done, you will be able to execute the symfony command from the path you want. As an
    advice, I must tell you that you should keep the installer updated executing the following command:
       ²http://symfony.com/doc/current/setup.html
       ³http://php.net/manual/es/phar.using.intro.php
       ⁴https://packagist.org/
       ⁵https://getcomposer.org/
    Chapter 1: installation and configuration                                                          5
    Awesome! Now that you own the installer in a global way, you can create your first Symfony
    project. So, execute the next command:
    The previous command will create the folder aupa_bilbao with every single dependency (needed
    to start working without losing a minute, everything automagically. If you see, at the end of the
    command there is a 2.8. This is telling the Symfony installer to download the latest 2.8 Symfony
    Standard. If you delete that version number, you will get the latest non-dev Symfony Standard. We
    must use the 2.8 version because the pre-built admin does not work (yet) with Symfony 3. But almost
    everything of the book works well with Symfony 3. If you’ve read the Symfony documentation or
    if you have used Composer previously, you should know that the vendor folder, placed in the root
    of the project, contains all the needed dependencies for your project. These depenencies are defined
    in the composer.json file which is also placed in the root of the project. We will be using this file
    a lot of times during the book. However, there is a very similar file named composer.lock which
    shows the blocked versions of the installed dependencies. This versions will be the ones installed
    when composer install command is executed.
    At this point, we already talked several times about Composer, but, what is Composer?
    Composer
    Composer is the excelence tool for the PHP dependency management that will help us to download
    and insert “packages” or libraries done by others in our project and then use them in a very simple
    way. If you have already used “npm” from Node or “bundler” from Ruby, this is very similar; if it’s
    not the case and you’ve never used them, don’t worry about it because you will have the oportunity
    to use them in this book.
    Same as with the Symfony installer, the best way to use the Composer binary is as an operative
    system command and, with this done, execute it wherever you want. To achieve the goal, execute
    the following commands:
    Talking about the Symfony project, thanks to his own installer, we already have the “base packages”
    downloaded within the vendor folder and the composer.json and composer.lock files well defined.
    But if the project you are working in a certain moment is a downloaded project or you are
    deploying a project to a server, you should execute the composer install command at the root
    of it. This command will install the concrete versions specified in the composer.lock file and,
    Chapter 1: installation and configuration                                                           6
    moreover, they should be the exact versions that were already tested and which are lack of bugs
    and incompatibilities.
    However, if you launch the composer update command, the process will ignore the blocking file
    and it will update to the latest possible version defined within the composer.json file, updating the
    blocking file when it finishes the download process.
    Hey! Do not forget to update your dependencies, every week there are lots of bug fixes fixed or
    some new features are added which will definitely improve the value of your project. Of course,
    keep your Composer updated too with the following command:
    MySQL Database
    I still find projects with a latin1 database codification, nowadays! I suggest you to avoid this
    codification and use an UTF-8 one for every project you develop. With Symfony, you won’t need
    to manually create the database because the console will create it for you, but in order to use the
    correct codification by default, you should do one of these two possibilities. First one: you need the
    doctrine-bundle the be least the 1.6 version (composer.json):
1 "doctrine/doctrine-bundle": "^1.6",
And then tell the Symfony framework to use the correct codification. Edit app/config/config.yml:
1   # Doctrine Configuration
2   doctrine:
3       dbal:
4           #...
5           default_table_options:
6               charset: utf8mb4
7               collate: utf8mb4_unicode_ci
8               engine: InnoDB
    The other possible option is to edit the my.cnf. This configuration file contains all the parameters
    for the MySQL database, and you should add the following two lines to the [mysqld] section:
1   [mysqld]
2   collation-server = utf8mb4_unicode_ci
3   character-set-server = utf8mb4
    With this done, you just need to tell Symfony what are your database connection parameters. Go to
    the app/config/parameters.yml file, which was automatically created with the installer, and edit
    the parameters that you can find there with the ones that fits your machine. Once modified, execute
    the next command in order to create the database:
    Chapter 1: installation and configuration                                                          7
1 bin/console doctrine:database:create
If you use Windows, you must place php just before the command.
    Good work! You already have your database created to start working. You just need to know one
    more thing: Symfony uses a series of folders that need to have writing permissions. Those directories
    will be used by your web server and your console user, so both of them should have permission to
    write within those folders.
    Writing Permissions
    This is a common mistake that I usually find in a lot of local deployments. A lot of people forget to
    give write permission to the var folder, where the cache folder resides, for example.
    In this link⁶ you can find how can give permissions to this folder, and if you are using an Ubuntu
    based Linux operative system, the following commands are OK for you:
    The first line stores in the variable HTTPDUSER the user who is executing our web server, because
    depending on the operative system and the server, this user varies. The other two lines will take
    care of giving that user and the console user (the user who will execute terminal commands) the
    right permission to write in the var folder.
    Given the pertinent permissions, you are about to finish the installation.
       ⁶http://symfony.com/doc/current/setup/file_permissions.html
       ⁷http://symfony.com/doc/current/setup/web_server_configuration.html
    Chapter 1: installation and configuration                                                              8
1 bin/console server:run
    If you read the terminal output, Symfony is telling you that you can see your project navigating to
    http://127.0.0.1:8000/. Regrettably, there is (almost) nothing to see at all, but we will solve this very
    soon.
    Development environment
    If you are going to develop with Symfony, I suggest you to install a development environment.
    Nowadays, the IDE that works like a charm for Symfony is PhpStorm⁸. There are some others like
    NetBeans or Aptana, or even editors like Sublime or Brackets that work perfectly. Try them all
    and choose the one that fits better your way of working. Personally, the one that I suggest you is
    PhpStorm, which will be present in a future “tip”.
    Tip 1
    In every single chapter I will give you an advice or a trick that will help you a lot with without any
    doubts. It depends on you whether to use this tips or not (use them!!). From now on, I will use the
    Symfony console quite a lot. As you could see, in order to execute the different commands you
    should type bin/console every single time, and if you want to change the execution environment
    from dev to prod, which is the default environment, you must add --env=prod at the end of every
    command you execute.
    With the following tip you will give a rest to your keyboard and, of course, to your hands:
    Those alias are not maintained after rebooting your machine, so search for the solution to keep
    those alias forever in your user (I should make you work a little bit, shouldn’t I?)
    In addition to the alias, there is no need to type the full command: it’s only needed the
    discriminatory part of it. What are you talking about? You will see this much brighter with an
    example, so look to the following command:
1 dev s:r
    The previous line will execute php bin/console --env=dev server:run. Why? Because there are
    just two commands that start with “s”: server and swiftmailer. But, inside them, there are just one
    that starts with “r”, which is server:run.
    Congratulations! You are more efficient than a lot of Symfony developers, who usually don’t know
    this little trick :)
       ⁸https://www.jetbrains.com/phpstorm/
Chapter 1: installation and configuration                                                          9
Summary
In this chapter we have learnt the correct way to deploy a new Symfony project in your computer to
start working with it. Thanks to the global installation of its installer and the Composer dependency
manager, the following new installations will be done in question of minutes, even seconds!
Chapter 2: third party bundles
One of the biggest things that Symfony has and it does share with a lot of open source applications
is that the community apports elements which enrich the framework. There exist several ways of
collaborating with Symfony, but the creation and mainteinance of bundles that give extra features
to the ones that come out of the box is a key point that makes Symfony so used worldwide. During
this chapter, we will learn the basis to install third party bundles, even though every single one
of them usually have different configuration between each other. Let’s start!
    versions, both with dependencies and Symfony, not all bundles support all Symfony versions and
    vice versa. It’s possible that sometimes you need to investigate a little bit in the documentation to
    select the right version of the bundle that fits your needs.
    After updating the file, you should launch the update Composer command in order to install the
    new dependencies:
1 composer update
    The next step is the bundle configuration. As with the composer version, the majority of the bundles
    have an specific configuration that you should add into the app/config/config.yml file. The
    concrete configuration should be given by the bundle creator, and it will allow you to parametrize
    your new dependency so it fits and works like a charm in your project, just in the way you want.
    Careful: there are also some bundles that require even more modifications in other framework files
    like the routing system, the services or the security related configuration files. Actually, every file is
    a configuration file in which you should paste a series of lines that you had previously copied from
    a README file or from the documentation.
    When this is done, the last step is the bundle activation. It seems like a stupid step but, when you
    work everyday with this stuff, this is the kind of action that you forget to do. In order to enable
    the new dependencies, you should edit the app/AppKernel.php file and add the instatiation lines
    needed that should also be given by the author in the documentation. If you can’t find those lines
    for whatever reason, you can go to the vendor folder and look for the main bundle file to find the
    specific class name to instantiate.
    Everything done! “Tatoo yourself” these steps in your brain source code, even though you can look
    whenever you want this chapter to remember everything. These steps are very easy and they will
    open you a lot of doors with possibilities for the Symfony environment, don’t you believe this? No
    problem, in the next chapters, without coding a single line, you will have a Symfony with everything
    you’ve ever wished.
    Tip 2
    I don’t know if this must be called a tip or a suggestion, but no matter what, it’s something that you
    should have in mind everyday as a developer. You have seen in this chapter that knpbundles.com
    is a good reference website for third party Symfony bundles. Don’t block yourself searching here
    what you need and when you need. You should visit this website with assiduity in order to know
    what is happening and what others are using. Be curious and keep yourself updated knowing the
    biggest part of the community stuff. You will earn a competitive advantage over all the other people
    that are isolated from the community or that are in their comfort zone reusing what they have been
    doing for years. In the world that you have decided to enroll, a year means lots of changes and
    modifications. You must be in this “ship of changes”, or you will be left in the harbour without even
    a “good bye”.
Chapter 2: third party bundles                                                                     12
Summary
In this chapter we have learnt the basis to install almost any third party bundle. We have established
some generic steps that will allow you to install and configure the majority of the community
developments. In the next chapter, we will start installing one of the most powerful and used bundles
in the Symfony world: the Admin Bundle from Sonata Project.
    Chapter 3: Admin Bundle
    We’ve seen in the previous chapter that there exists an active community that apports a lot of
    interesting elements to the Symfony environment. Some of them work much harder than others,
    and the Sonata project is with no doubts one of the great ones to follow. In their bundle stack¹⁰
    you will be able to find out several awesome solutions. In the previous link you can read information
    about the bundles that they are working actively at the moment so you can fill your neurons with
    interesting stuff. Moreover, during this book, we will try some of these bundles, so you will learn
    how to install them, how to configure them and, of course, how to use them. The rest of the bundles
    are your homework. Let’s go!
    Be careful with the version and check the Sonata Admin GitHub repository for the correct version
    that you need, always using the latest stable branch.
    As an advice, I will tell you to keep attention to the terminal each time you intall a new dependency,
    because you can read lines such as:
    You should at least read the suggested bundle documentation because it may be very interesting for
    your project due to its strict relation with something you have just installed.
    The next step is installing the bundle that allows the admin to manage the MySQL database:
      ¹⁰https://sonata-project.org/bundles/
    Chapter 3: Admin Bundle                                                                            14
    After that, you need to enable the bundle and its dependencies. In the previous chapter we did this in
    the last step. Why? Just because if you enable the bundle without having finished the configuration,
    a lot of errors will come up (if the bundle needs a configuration, indeed), so if you leave the
    configuration in any of the intermediate steps and you want to keep on doing what you were doing
    right before the installation process, you will have to deactivate the bundle again and finish the
    installation in another moment. We are going to finish it right now so the order doesn’t really
    matter, but keep in mind that you can do this step at the end. To enable the required dependencies,
    edit the app/AppKernel.php file adding these lines just before the AppBundle:
1   new   Knp\Bundle\MenuBundle\KnpMenuBundle(),
2   new   Sonata\CoreBundle\SonataCoreBundle(),
3   new   Sonata\BlockBundle\SonataBlockBundle(),
4   new   Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
5   new   Sonata\AdminBundle\SonataAdminBundle(),
    For the next step, we have to configure the different bundles that we have just enabled. Everything
    you need is in the official documentation, and as a teacher said to me: “a good developer knows how
    to copy and paste effortless”. Edit the app/config/config.yml file and add the next configuration
    lines:
1   sonata_block:
2       default_contexts: [cms]
3       blocks:
4           sonata.block.service.text:
5           sonata.admin.block.admin_list:
6               contexts: [admin]
7           sonata.admin.block.search_result:
8               contexts: [admin]
    Furthermore, we need to activate the translator service. To achieve this, in the same config.yml
    file, you should delete the preceeding “#” to the translator line at the top of the file. Did you find
    it? Cool, let’s continue.
    At the moment, we will do the first “fireproof”: copy the assets to the public folder and clear the
    cache. If any of the following commands gives you an error, I have to tell you that you have done
    something wrong in any of the previous steps:
    Chapter 3: Admin Bundle                                                                                 15
    To understand those lines you should have read the Symfony documentation (what? You haven’t
    read it yet?!?). For the latest ones: the first line of the previous code copies the assets (resources such
    as JavaScript files, CSS, images, …) from the non public folders (src/..., vendor/, etc.) to the public
    web folder, in this case, creating a symbolic link (the best option when we are in the development
    stage, don’t do this at production); the second line simply clears the cache in order to regenerate the
    necessary elements for a correct rendering.
    Amazing! We already have the admin installed but… it’s not funcional yet. The next part of
    the configuration will allow us to access the admin from our browser. So, we need to edit the
    app/config/routing.yml file and add the routes that already come with the vendor and that will
    make the development reachable from a browser. Add the next configuration lines at the top of the
    file:
1   admin:
2       resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
3       prefix: /admin
4
5   _sonata_admin:
6       resource: .
7       type: sonata_admin
8       prefix: /admin
    Do you know that you already have an admin working? If you have the server up and running, just
    navigate to http://127.0.0.1:8000/admin and you will see your empty and deserted administration
    panel. I guess you have some questions right now:
    Keep calm young padawan, every single part will come at its moment, and right now it’s just the
    moment to answer the very first point of the previous list, because in the next chapter we will
    install another bundle that uses this admin. With it, we will have these features out of the box
    automagically:
• Etc.
     The best of all of this is that the user bundle installation is just 10 minutes long, and the power that
     it gives you is awesome. You will never take care again of coding the user management anymore for
     any of your websites.
 1   <?php
 2   // src/AppBundle/Entity/Product.php
 3
 4   namespace AppBundle\Entity;
 5
 6   use Doctrine\ORM\Mapping as ORM;
 7
 8   /**
 9     * @ORM\Entity
10     * @ORM\Table(name="product")
11     */
12   class Product
13   {
14        /**
15         * @ORM\Column(type="integer")
16         * @ORM\Id
17         * @ORM\GeneratedValue(strategy="AUTO")
18         */
19        protected $id;
20
21        /**
22         * @ORM\Column(type="string", length=100)
23         */
24        protected $name;
25
       ¹¹http://symfony.com/doc/current/doctrine.html#add-mapping-information
     Chapter 3: Admin Bundle                                                                               17
26       /**
27        * @ORM\Column(type="decimal", scale=2)
28        */
29       protected $price;
30
31       /**
32        * @ORM\Column(type="text")
33        */
34       protected $description;
35   }
     Remember to generate the getters and the setters and update your database right after creating or
     modifying any of your entities:
     The next thing you have to do is declare the admin class that will be the CRUD for this entity. If
     you come from previous versions of Symfony, you will know that the dependency injection of every
     bundle loaded a file called services.yml. Personally, I like to edit that dependency injection to load an
     admin.yml file which also loads services, but it allows us to keep the code well “ordered”: everything
     in its propper place. In this chapter’s tip, I will share with you how to achieve this goal, but for this
     example, you will have to edit the app/config/services.yml file to load our admin class from there:
 1   services:
 2       sonata.admin.product:
 3           class: AppBundle\Admin\ProductAdmin
 4           tags:
 5               - { name: sonata.admin, manager_type: orm, group: "Content", label: \
 6   "Product" }
 7           arguments: [~, AppBundle\Entity\Product, ~]
     Excelent. As you can read from the previous code, we need to create a ProductAdmin class that is a
     copy & paste from the documentation one, modifying just the entity fields:
     Chapter 3: Admin Bundle                                                          18
 1   <?php
 2   // src/AppBundle/Admin/ProductAdmin.php
 3
 4   namespace AppBundle\Admin;
 5
 6   use   Sonata\AdminBundle\Admin\AbstractAdmin;
 7   use   Sonata\AdminBundle\Datagrid\ListMapper;
 8   use   Sonata\AdminBundle\Datagrid\DatagridMapper;
 9   use   Sonata\AdminBundle\Form\FormMapper;
10
11   class ProductAdmin extends AbstractAdmin
12   {
13      // Fields to be shown on create/edit forms
14      protected function configureFormFields(FormMapper $formMapper)
15      {
16          $formMapper
17               ->add('name')
18               ->add('price')
19               ->add('description')
20          ;
21      }
22
23      // Fields to be shown on filter forms
24      protected function configureDatagridFilters(DatagridMapper $datagridMapper)
25      {
26          $datagridMapper
27               ->add('name')
28               ->add('price')
29          ;
30      }
31
32      // Fields to be shown on lists
33      protected function configureListFields(ListMapper $listMapper)
34      {
35          $listMapper
36               ->addIdentifier('id')
37               ->add('name')
38               ->add('price')
39               ->add('description')
40               ->add('_action', 'actions', array(
41                   'actions' => array(
42                       'edit' => array()
     Chapter 3: Admin Bundle                                                                              19
43                       )))
44               ;
45        }
46   }
     We made it! If you visit http://127.0.0.1:8000/admin again, you will be able to see the product section.
     Try to create, edit, filter and the rest of the available actions that it gives you for free.
     From now on, adding more stuff to your administration panel is a “piece of cake”: create an admin
     class and enable it in the services.yml file, knowing that the PHP class and the service declaration
     are practically an exact copy & paste from every other admin that you have previously created.
     In this example, I added you some modifications that are not in the official documentation, like
     the edit button in the list. With this admin, you also will be able to add your own buttons that
     are associated to your own controllers, so use the admin for everything that fits to you, and adapt
     the admin so it fits the rest of the actions that doesn’t come out of the box. How do you do this?
     Documentation and trials, come on! By now, here you can read the Sonata Admin Bundle base
     configuration¹². Try to change the title, the logo and other elements that you need.
     Tip 3
     It’s possible that you like to have the admin part completely centralized in your bundle and you don’t
     want to depend on the services file that it’s in the app folder. This is achieved with the dependency
     injection in a very simply way. If you create a new bundle, this class is already created by default.
     You can try this with the following command:
1 bin/console generate:bundle
     But without any doubts, the best way to learn is trying it yourself (and breaking everything). To do
     so, here you have the necessary documentation in this link¹³. Remember to modify the part of XML
     with YAML:
              Note: the previous link could change its content because the bundle documentation is
              in a re-estructuration process.
         ¹²https://sonata-project.org/bundles/admin/master/doc/reference/configuration.html
         ¹³http://symfony.com/doc/current/bundles/extension.html
Chapter 3: Admin Bundle                                                                           20
Other possibilities
There are other possibilities to create administration panels effortless in Symfony. Javier Eguiluz¹⁴
does have one of them, nowadays with more GitHub stars than the recently shown Sonata
admin. You should, at least, try it and see if you like it and if it fits better with your projects:
https://github.com/javiereguiluz/EasyAdminBundle¹⁵.
Summary
In this chapter we have learnt to install the Admin bundle from the Sonata project and we have
created our first admin class applied to the admin panel. During the next chapter we will install the
user management system based on this administration development that will give us the complete
control over them, and it will also secure our recently installed admin.
  ¹⁴https://github.com/javiereguiluz
  ¹⁵https://github.com/javiereguiluz/EasyAdminBundle
    Chapter 4: User Bundle
    In this chapter we will extend the admin we’ve just created in the previous chapter and, in
    addition, we will give some vitamins to our base project. With the Sonata User Bundle we will
    have a complete user management thanks to the incorporation of a well known bundle called
    FOSUserBundle. Sonata simply gives you a layer over this bundle that improves the integration
    between its admin adding some other extra features. As we did in the previous chapter, the steps
    we are going to follow are: installation, configuration and then learn how to use it. Let’s start!
    We already have the requirements correctly installed, so we can start installing Sonata User Bundle.
    In order to make it work under Symfony 3, you must add the following lines:
1 "sonata-project/user-bundle": "4.x-dev"
    NOTE: as soon as it becomes stable, I will edit the book with the final versions.
    As you’ve read in the chapter introduction, this bundle is no more than a layer over FOSUserBundle.
    It uses FOSUserBundle version 1 stable release. There is an active branch working with the
    FOSUserBundle version 2, but nowadays is not stable yet, so that branch won’t be released until
    FOSUsedrBundle 2 gets tagged (please, check it!). What you need right now is to enable both bundles,
    as always, editing the app/AppKernel.php file:
1   new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
2   new FOS\UserBundle\FOSUserBundle(),
3   new Sonata\UserBundle\SonataUserBundle(),
    The following configuration steps are usually the ones that make people fail at installing the bundle,
    so please be aware. The first step is adding the following lines to the app/config/config.yml
    configuration file:
      ¹⁶https://sonata-project.org/bundles/user/master/doc/reference/installation.html
     Chapter 4: User Bundle                                                                            22
 1   fos_user:
 2       db_driver:       orm
 3       firewall_name: main
 4       user_class:      Sonata\UserBundle\Entity\BaseUser
 5       group:
 6           group_class:    Sonata\UserBundle\Entity\BaseGroup
 7           group_manager: sonata.user.orm.group_manager
 8       service:
 9           user_manager: sonata.user.orm.user_manager
     In addition, in the dbal configuration located in the doctrine configuration section, you must add
     the last two lines of the following configuration block. Be careful with the indentation because it’s
     a YAML file:
 1   doctrine:
 2       #...
 3       dbal:
 4           types:
 5               json: Sonata\Doctrine\Types\JsonType
     Be sure to have the auto_mapping parameter activated in the orm piece of configuration, in the
     same part as the dbal section.
     Lastly, modify the sonata_block part of the configuration in order to some more lines. This is the
     final result:
 1   sonata_block:
 2       default_contexts: [cms]
 3       blocks:
 4           sonata.user.block.menu:
 5           sonata.user.block.account:
 6           sonata.block.service.text:
 7           sonata.admin.block.admin_list:
 8               contexts: [admin]
 9           sonata.admin.block.search_result:
10               contexts: [admin]
     Amazing! Now we can load the new routes that will allow us to sign in, request a new password
     or see our platform profile. We will also add some routes that belong to the security part of the
     bundle between Sonata User and Sonata Admin (and without coding a line!). You just have to add
     the following lines to the app/config/routing.yml file:
     Chapter 4: User Bundle                                                                          23
 1   # Sonata User
 2   sonata_user_admin_security:
 3       resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
 4       prefix: /admin
 5
 6   sonata_user_admin_resetting:
 7       resource: '@SonataUserBundle/Resources/config/routing/admin_resetting.xml'
 8       prefix: /admin/resetting
 9
10   # FOS User
11   fos_user:
12       resource: '@FOSUserBundle/Resources/config/routing/all.xml'
13
14   fos_user_group:
15       resource: '@FOSUserBundle/Resources/config/routing/group.xml'
16       prefix: /group
     Finally (for now, do not cry victory), you have to configure the security part of your website. The
     file that owns this configuration part of the platform is app/config/security.yml. You can delete
     the whole content of the file and add the code that I share with you here very nicely:
 1   security:
 2       encoders:
 3           FOS\UserBundle\Model\UserInterface: bcrypt
 4
 5        role_hierarchy:
 6            ROLE_ADMIN:       [ROLE_USER, ROLE_SONATA_ADMIN]
 7            ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
 8            SONATA:
 9                - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT
10
11        providers:
12            fos_userbundle:
13                id: fos_user.user_manager
14
15       firewalls:
16           # Disabling the security for the web debug toolbar, the profiler and Ass\
17   etic.
18           dev:
19                pattern: ^/(_(profiler|wdt)|css|images|js)/
20                security: false
21
     Chapter 4: User Bundle                                                             24
64
65           # Secured part of the site
66           # This config requires being logged for the whole site and having the ad\
67   min role for the admin part.
68           # Change these rules to adapt them to your needs
69           - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
70           - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
     I recommend you to read and understand it because it’s not complicated at all, and remember to
     contrast everything that you don’t understand with the official Symfony documentation, in which
     you will find answers for sure. But now, it’s time to see some magic. With your server up and
     running:
1 bin/console server:run
     Navigate to http://127.0.0.1:8000/admin/. You should see a fantastic login window to access your
     admin, and you should also see a fantastic database error when you try to interact with that login
     form. At this point, these things could happen:
         • You see everyting alright, even the database error: dont’t worry, you are going in the right
           way.
         • You see the form and the error, but the page styles are not ok: try to clean the cache and
           install the assets.
         • I don’t see anything and/or you do have console errors: one, two, three… start again the
           chapter!
     We’ve finished configuring the admin and the Sonata User Bundle. The next step: generate the user
     and the group structure so we can interact with the previous login form. We are going to create our
     admin user.
    The previous line will generate another bundle in the src folder, and because it’s a new bundle, you
    have to enable it. Open and edit the app/AppKernel.php file, adding the next line:
1 new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
    Now, you have to change a little piece of the configuration you added in the previous section of
    the chapter, the one that is related to the user and the group classes. Modify the user_class and
    group_class parameters within the fos_user configuration in the app/config/config.yml file, just
    as it is in the following configuration block:
1   fos_user:
2       db_driver:       orm # can be orm or odm
3       firewall_name: main
4       user_class:      Application\Sonata\UserBundle\Entity\User
5       group:
6           group_class:    Application\Sonata\UserBundle\Entity\Group
7           group_manager: sonata.user.orm.group_manager
8       service:
9           user_manager: sonata.user.orm.user_manager
    There you have! Don’t you believe so? Execute the following command in a terminal to see what is
    going to be added automatically to the database structure:
When you finish the celebration, execute the changes in order to create the database structure:
    Now you can navigate to the login form and it shouldn’t launch a database error, but… you don’t
    have the needed role to log in because you don’t even have a user. Let’s solve this problem.
1 app/console fos:user:create
    If you’ve followed all the steps, you will have a user that will be able to log in to your website,
    but if you try it, you will notice that you are not allowed to see the admin: you don’t have the
    correct permission! Why? If you go a little backwards and read the access_control section within
    the security file, you will see that the ROLE_ADMIN role is needed in order to access the admin
    area. We will take advantage of the actual moment to create a super admin assigning the ROLE_-
    SUPER_ADMIN role to the recently created user.
1 app/console fos:user:promote
    Right after assigning the role I told you, you will see that if you have previously logged in, you
    won’t be able to access. This is because, once you are logged in, the user roles are serialized into the
    session, so you do have to log out going to http://127.0.0.1:8000/logout (or in the profile bar at the
    bottom) and navigate to http://127.0.0.1:8000/admin again.
    What do you you think? With these first chapters of the book I should have conquered you. If it’s
    not like that, don’t worry, we have a long journey together so I can reach your heart and win this
    fight.
    It’s time you stop reading for now and start working with what we have installed in these chapters.
    The most important thing: open the routing file and navigate to every single route that is loaded
    there. Remember that the routing file is app/config/routing.yml. Some fantastic routes are login,
    register, profile, etc. You can start with this last one going to http://127.0.0.1:8000/profile/.
    Tip 4
    A lot of people forget that Sonata User Bundle is no more than a layer above FOSUserBundle. This
    means that every single configuration that is allowed for the FOS bundle it’s also working for the
    recently installed Sonata bundle. For example, the email confirmation feature that you can see in
    this link¹⁷. You just have to add this piece of configuration and your users will have to confirm their
    accounts by clicking to an activation link that the bundle will automatically send to their email
    account. You won’t have to worry about this feature from now on, you just have to adapt the email
    template.
      ¹⁷https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/emails.rst
    Chapter 4: User Bundle                                                                                 28
1   fos_user:
2       # ...
3       registration:
4           confirmation:
5               enabled: true
    Besides, I know that you are thinking about “how ugly these white pages are”, and I also think so.
    These won’t fit in any project, but you are able to modify the layout so it fits your base templating
    and also add and/or modify every single element that appears in those pages. All this configuration
    is here¹⁸. But, I recommend you to write down the link and wait, because in the following chapters
    we will learn about the base templating for our project and for third party bundles so they fit our
    needs.
    Summary
    In this chapter we have installed and configured Sonata User Bundle, a bundle that will allow us
    to have a complete user management at a database and web structure levels. Also, with the Admin
    Bundle, the admin part will is well covered too. With now doubts, the installation of those Sonata
    bundles allow us to save a lot of time in our project so we can spend the time to the most important
    part of it. We just have to worry about overriding the parts of the third party bundles that we need
    to modify.
      ¹⁸https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.rst#next-steps
Chapter 5: MopaBootstrap
We already have our admin and our user system up and running. But, as far as you have noticed,
the base design of the installed bundles until now is very simple.
It’s time to give some life and color to our project and prepare a good base for the future of it. We
could start writing CSS files from scratch, or even download some CSS framework and insert it in
our project as assets in the templates (if this doesn’t sound familiar to you, it’s time to stop and
read the convenient documentation chapters). But I have to suggest you something much better that
derives into the next questions:
    • Start writing CSS from scrath is like committing suicide. It would be an interesting didactical
      exercise… but if you want to learn CSS from the very beginning, you must learn it with other
      book because I won’t cover it in this one.
    • Inserting a framework as a dwonloaded asset is perfectly valid, but… why do we have to do
      this in this way when we already have a dependecy management system?
    • CSS… are you in the 1990’s? Don’t you ever use CSS nowadays without a preprocessor.
      Working without variables or functions will decrease your work efficency drastically.
So, what are we going to do? We will use Composer in order to install MopaBootstrapBundle, a
bundle that will allow us to have a base template to extend and which will already have Bootstrap
configured. Moreover, it contains a variety of extra components and it will allow us to have sassy
files (.scss) to work directly with them. How? There are a lot of ways to “walk throught the path”.
Symfony did have one of them preinstalled until version 2.7: Assetic. After a long time using this
technology, I realized that it had a lot of shortcomings and my development was becoming very
slow. After a series of intermediate changes, I finally started using Gulp and a new world full of
possibilities came up to me. In the next chapters, I will show you this world to you, but at this
moment, let’s install MopaBootstrap.
      NOTE: if you are an experienced developer, you will know that coupling your code to
      a huge bundle like MopaBootstrap is not the best decision. This chapter will help you
      a lot if you are starting with the frontend design or maybe if you are an intermediate
      developer. In the last chapter, I will show you how to cover this “coupling” the best way.
Installing MopaBootstrapBundle
As always, the first thing that we have to do is editing the depedency definition file composer.json.
This time, instead of using the composer require command, we can edit the file directly, so you can
learn how to do it this way too. Let’s add the following lines to the require section (be sure to check
the right versions in the Packagist website):
     Chapter 5: MopaBootstrap                                                                        30
 1   "mopa/bootstrap-bundle": "^3.0",
 2   "twbs/bootstrap-sass": "^3.3.0"
     Furthermore, in the same file, you should add some hooks at the end of it. They are just Symfony
     commands that will be executed when Composer finishes the composer install or the composer
     update processes. In the piece of code that you see right under this sentence, I just leave you the
     line that you must add in each section:
 1   {
 2       "scripts": {
 3           "post-install-cmd": [
 4               ...
 5               "Mopa\\Bundle\\BootstrapBundle\\Composer\\ScriptHandler::postInstall\
 6   SymlinkTwitterBootstrapSass"
 7           ],
 8           "post-update-cmd": [
 9               ...
10               "Mopa\\Bundle\\BootstrapBundle\\Composer\\ScriptHandler::postInstall\
11   SymlinkTwitterBootstrapSass"
12           ]
13       }
14   }
     Right before downloading anything, we must enable and configure the bundle because the hooks
     will get launch as soon as we launch the Composer process. Edit the app/AppKernel file to enable
     the bundle:
1 new Mopa\Bundle\BootstrapBundle\MopaBootstrapBundle(),
     We also need to modify the app/config/config.yml file to insert the configuration that is given to
     us in the bundle documentation:
 1   mopa_bootstrap:
 2       form: ~ # Adds twig form theme support
 3       menu: ~ # enables twig helpers for menu
     Excelent! Now you can launch the update command to get the depencendies downloaded and
     execute the hook that we inserted earlier on:
    Chapter 5: MopaBootstrap                                                                             31
1 composer update
    It is possible that you see other bundles getting updated. This is good (if you have the control),
    specially if you work with dev-master versions (never do that in a production environment). You
    should update your bundles periodically. If everything is correct, you should see something like this
    at the end of the process:
1 http://127.0.0.1:8000/
    If you have opened the controller source code, you will see that the route is getting loaded with
    an annotation. For everything that you code and for everything that you will be doing with me
    in the next chapters, I recommend you using annotations; in order to load third party routing files,
    creating redirections or loading a template without a controller, you should use the routes as we
    have been already doing.
    In this chapter we are just going to change the base template so it extends the one that Mopa-
    Bootstrap gives us for free, and we will explain how it works. In the next chapter, we will
    prepare the Sass part with Gulp so we can start modifying the styling. Let’s edit the app/Re-
    sources/views/base.html.twig file and leave it like this:
1 {% extends 'MopaBootstrapBundle::base.html.twig' %}
    What? Is that all? Correct, and if you visit the homepage again, you will see that we broke the entire
    homepage… more or less. If you open the source code, you will see that you have a title, there exists
    a meta with the viewport and some other details. But, continuing with the development, you will
    see that the base template that you are extending contains all this code:
      ¹⁹http://symfony.com/doc/current/templating.html
     Chapter 5: MopaBootstrap                                                            32
43   {% block body_tag %}
44   <body>
45   {% endblock body_tag %}
46
47   {% block body_start %}
48   {% endblock body_start %}
49
50   {% block body %}
51       {% block navbar %}
52       <!-- No navbar included here to reduce dependencies, see https://github.com/\
53   phiamo/MopaBootstrapSandboxBundle/blob/master/Resources/views/base.html.twig for\
54    howto include it -->
55       {% endblock navbar %}
56
57       {% block container %}
58       {% block container_div_start %}<div class="{% block container_class %}contai\
59   ner{% endblock container_class %}">{% endblock container_div_start %}
60           {% block header %}
61           {% endblock header %}
62
63           {% block content_div_start %}<div class="content">{% endblock content_di\
64   v_start %}
65               {% block page_header %}
66               <div class="page-header">
67                     <h1>{% block headline %}Mopa Bootstrap Bundle{% endblock headl\
68   ine %}</h1>
69               </div>
70               {% endblock page_header %}
71
72                  {% block flashes %}
73                  {% if app.session.flashbag.peekAll|length > 0 %}
74                  <div class="row">
75                      <div class="col-sm-12">
76                      {{ session_flash() }}
77                      </div>
78                  </div>
79                  {% endif %}
80                  {% endblock flashes %}
81
82                  {% block content_row %}
83                  <div class="row">
84                      {% block content %}
      Chapter 5: MopaBootstrap                                                            34
 85                      <div class="col-sm-9">
 86                          {% block content_content %}
 87                          <strong>Hier könnte Ihre Werbung stehen ... </strong>
 88                          {% endblock content_content %}
 89                      </div>
 90                      <div class="col-sm-3">
 91                          {% block content_sidebar %}
 92                          <h2>Sidebar</h2>
 93                          {% endblock content_sidebar %}
 94                      </div>
 95                      {% endblock content %}
 96                  </div>
 97                  {% endblock content_row %}
 98
 99             {% block content_div_end %}</div>{% endblock content_div_end %}
100
101             {% block footer_tag_start %}
102             <footer>
103             {% endblock footer_tag_start %}
104
105           {% block footer %}
106           <p>© <a href="http://www.mohrenweiserpartner.de" target="_blank">Mo\
107   hrenweiser & Partner</a> 2011-2015</p>
108           {% endblock footer %}
109
110           {% block footer_tag_end %}
111           </footer>
112           {% endblock footer_tag_end %}
113       {% block container_div_end %}</div><!-- /container -->{% endblock container_\
114   div_end %}
115       {% endblock container %}
116
117        {% block body_end_before_js %}
118        {% endblock body_end_before_js %}
119
120       {% block foot_script %}
121       {# To only use a subset or add more js overwrite and copy paste this block
122       To speed up page loads save a copy of jQuery in your project and override th\
123   is block to include the correct path
124       Otherwise the regeneration is done on every load in dev more with use_contro\
125   ller: true
126        #}
      Chapter 5: MopaBootstrap                                                                              35
      What the hell! What is this about? Let’s break this into pieces. Every {% block %} tag will let you
      override the parts that the block is made of or will let you add more stuff. This will allow us to
      add our CSS and JS files, modify the meta fields, override just the <body> tag to add, for example,
      a new class to it, etc. The base template is very complete, and it should fit your needs for almost
      every project. What happens if it doesn’t fit your needs? Feel free to override whatever you want,
      or even make a new one from scratch. And… what about the rest? Blocks, blocks and more blocks,
      some of them with basic default components that you can use; others with a very short JavaScript
      content that will trigger the tooltips and the popovers that Boostrap contains; a lot of base code that I
      leave you alone with it so you read it calmly. Before continuing, I suggest you a small change to start
      the homepage from the very beginning: Edit app/Resources/views/default/index.html.twig and
      leave it like this:
  1   {% extends 'base.html.twig' %}
  2
  3   {% block content_content %}
  4       Homepage.
  5   {% endblock %}
      If you refresh the homepage, what has happened? Now we just overrided the central piece of the
      template and we can see a heading and a footer. It’s time to change the texts that we don’t need
      anymore, so let’s edit the app/Resources/views/base.html.twig base template and adapt it to what
      we want:
     Chapter 5: MopaBootstrap                                                                           36
 1   {% extends 'MopaBootstrapBundle::base.html.twig' %}
 2
 3   {% block title %}Aupa Bilbao!{% endblock %}
 4
 5   {% block headline %}Aupa Bilbao!{% endblock headline %}
 6
 7   {% block footer %}
 8   <p>Sexy footer 2016</p>
 9   {% endblock footer %}
     We refresh the page and… et voilà! We modified the pieces that didn’t belong to our new website
     so it fits our needs (didactical, of course). But as you have noticed, where are the Bootstrap default
     styles? We must insert them with Sass.
     There is a “base_sass_3.2” template but it uses Assetic for the SCSS to CSS precompilation. We will
     take a further step adding Gulp, a technology that will make this precompilation process but it will
     also give us some extra features that everybody must need.
     Tip 5
     MopaBootrap has a sandbox²⁰ that you can deploy in your computer or just visit it online to see
     what it contains. In this tip, I’m going to help you to build your own menu thanks to the knp-menu
     bundle that you already have as a Sonata dependency. It may be useful for you or you may want to
     keep the control of the menus in your templates manually. Up to you.
     The first thing we are going to do is creating the src/AppBundle/Menu folder, and inside it, we should
     create the Builder.php file with the following content:
 1   <?php
 2
 3   namespace AppBundle\Menu;
 4
 5   use Knp\Menu\FactoryInterface;
 6
 7   class Builder
 8   {
 9       public function mainMenu(FactoryInterface $factory, array $options)
10       {
11           $menu = $factory->createItem('root', array(
12               'navbar' => true,
13           ));
       ²⁰http://bootstrap.mohrenweiserpartner.de/
     Chapter 5: MopaBootstrap                                                                         37
14             $menu->addChild('Home', array(
15                 'icon' => 'home',
16                 'route' => 'homepage',
17             ));
18             $dropdown = $menu->addChild('Subpages', array(
19                 'dropdown' => true,
20                 'caret' => true,
21             ));
22             $dropdown->addChild('Subpage 1', array('route' => 'homepage'));
23             $dropdown->addChild('Subpage 2', array('route' => 'homepage'));
24             $dropdown->addChild('Subpage 3', array('route' => 'homepage'));
25
26             return $menu;
27        }
28   }
     The next step is inserting our menu in the base.html.twig template. The code block that you see
     under these lines is the one that you need, if you want to keep your template ordered, insert it
     between the title and the headline blocks:
 1   {% block navbar %}
 2       {% embed '@MopaBootstrap/Navbar/navbar.html.twig' with {'fixedTop': true } %}
 3           {% block brand %}
 4               <a class="navbar-brand" href="#">Aupa</a>
 5           {% endblock %}
 6
 7           {% block menu %}
 8               {{ mopa_bootstrap_menu('AppBundle:Builder:mainMenu', {'automenu':'na\
 9   vbar', 'currentClass': ''}) }}
10           {% endblock %}
11       {% endembed %}
12   {% endblock navbar %}
     Wonderful! Now, if you reload the homepage you will see the menu that we just created with a
     bunch of links and sublinks that will keep you in the home. This menu is very ugly, but we will take
     care of the styling magic in the next chapter.
     Summary
     In this chapter we have learnt to install the MopaBootstrapBundle and we have created a small base
     template without styling. We have discarded the base Sass template just because it uses Assetic. The
Chapter 5: MopaBootstrap                                                                               38
aim of this book is that you learn to use the latest technologies (or at least, the ones that I used while
I was writing this book), and because of that we will be using Gulp to precompile our SCSS files. In
future chapters, we will give Gulp some vitamines and we will get a lot of usefull features.
    Chapter 6: Gulp (1)
    Step by step, our base project is taking shape. But, if I had to highlight a chapter until now, probably
    I would highlight this one. Curiously, this is the first one in which we won’t work with Symfony
    at all; instead of it, we will support our development on external tools to make the SCSS to CSS
    precompilation and to give some vitamines to this process with a bunch of improvements.
    If you haven’t worked with any preprocessor until this day, don’t worry. SCSS is a CSS3 superset.
    What is this about? Every single line that previously worked on CSS it will now work with SCSS.
    So… how will this help me? In this link²¹ you can read a short guide so you can see the power of
    Sass: variables, mixins and the rest of the terminology. Once you start using it, you won’t go back
    again.
    In this chapter we are going to learn how to create our SCSS files and use them while we are
    developing, so you won’t need to precompile over and over again once you make a change. Moreover,
    in the tip I will explan you how the SCSS files are usually organized to work in an ideal way.
    Installation
    Like in every chapter, we have to install the needed requirements so the tools can work correctly.
    First of all, you should know that in order to install Gulp²² you should have previously installed
    Node.js²³ and npm²⁴. The first of them allows us to have a JavaScript server environment thanks
    to the V8 Google engine; the other one is a dependency manager for Node and a lot of extra tools.
    These are the commands you must use in an Ubuntu based operative system:
    Nothing that scares you at this moment, right? The next step is installing Gulp from npm. This
    dependency manager has the ability to install a package as an operative system binary. In case of
    Gulp, we need to install the binary in a global scope so we can execute the command everywhere, but
    also as a local dependency need by our gulpfile.js file. In order to install it as a global dependency,
    execute the next command:
      ²¹http://sass-lang.com/guide
      ²²http://gulpjs.com/
      ²³https://nodejs.org/
      ²⁴https://www.npmjs.com/
    Chapter 6: Gulp (1)                                                                                  40
    We have now everything we need installed, so let’s go a further step. But… you may think that in
    order to transform from SCSS to CSS you also need Ruby. With LibSass you just need Node.js in it’s
    a lot quicker than the Ruby solution. We will install some Ruby gems in a future chapter, but for
    now on, we have everything that we need installed.
    Next step: create our first SCSS file.
1 @import '../../../../web/bundles/mopabootstrap/sass/mopabootstrapbundle-3.2';
    If you are new to Sass, what this line does is importing another SCSS file which contains everything
    that is necessary for Bootstrap thanks to the previously installed bundle. Of course, we will be
    adding more lines to this file.
    Now that we have our ultra-complex SCSS file ready, it’s time to transform it so our browser can
    understand what we want. We will be using a Gulp task for this aim, but first, we need to take some
    previous steps that will help us in a short term period and also to the other possible developers that
    want to deploy and work in this project. Execute the next command in the root of the project and
    start typing what is asked to you:
1 npm init
    You should have a file called package.json when you finish, with a content more or less like this
    one:
    Chapter 6: Gulp (1)                                                                                41
1   {
2       "name": "aupa_bilbao",
3       "version": "0.0.1",
4       "description": "Aupa Bilbao",
5       "main": "gulpfile.js",
6       "author": "Jon Torrado",
7       "license": "MIT"
8   }
    Does this sound familiar to you? Here we are going to define the needed NPM packages in order to
    make our Gulp tasks work. As with Composer, when you deploy this project into a new computer or
    mabye another person is going to work with you, just typing npm install will make npm read this
    file and download all the needed packages. But… what do we need? Wow, you are full of energy!
    Let’s start with the first package. Even that we have it installed in a global way, it’s necessary to
    install it as a local dependency as I told you earlier on:
    Did you see the change in the package.json file? This is because of the --save-dev flag added at
    the end of the command that allows to save the dependency in the dependency definition file after
    installing it. As I told you, if a new developer comes to the project and he/she wants to deploy it in
    his/her computer, just just need to install npm and then type npm install at the root of the project
    and, let’s work! Now, we must install the rest of the packages that we are going to use:
    With this package, we already have everything that we need installed. Last step: define a Gulp task
    that will make the precompilation work for us. In order to achieve this, create a gulpfile.js file in
    the root of the project with the following content:
    We made it! Execute the task, a task that we have called sass, to create the CSS file that our browser
    will understand:
    Chapter 6: Gulp (1)                                                                                 42
1 gulp sass
    If everything went all right, you should have a web/css/app.css file ready to be included in your
    base template. Let’s edit app/Resources/views/base.html.twig and add that CSS file in the top
    part of the file, always taking care of the blocks that we can override from the base MopaBootstrap
    template that we are extending:
1   {% block head_style %}
2       <link href="{{ asset('css/app.css') }}" type="text/css" rel="stylesheet" />
3   {% endblock head_style %}
    It’s time to see what we have created. Open your browser and type down the next URL: http://127.0.0.1:8000/.
    Do you see it? We already have the styles that Bootstrap gives for free to us. Everything is in the
    right place but… there is no interaction. Why? Easy: we have to include the JavaScript files. We will
    be doing this later on, because at this moment, I have to help you to become a Gulp jedi master.
    After this, we have to edit our gulpfile.js file, and leave it like this:
     Chapter 6: Gulp (1)                                                                                 43
     Do you see what we have done? Firstly, we transform the SCSS files to a single app.css file. Then,
     we pipe this same exit to rename the file appending a .min to it, minifying the result with cssnano
     and leaving it in the same exit folder, resulting in two files: app.css and app.min.css. Hey! Don’t
     forget to edit your app/Resources/views/base.html.twig template file so it fits this change:
 1   {% block head_style %}
 2       {% if app.debug %}
 3       <link href="{{ asset('css/app.css') }}" type="text/css" rel="stylesheet" />
 4       {% else %}
 5       <link href="{{ asset('css/app.min.css') }}" type="text/css" rel="stylesheet"\
 6    />
 7       {% endif %}
 8   {% endblock head_style %}
     I’ve added a little logic that will allow us to load each file depending on the environment. So, if you
     browse to http://127.0.0.1:8000/app.php/, you will see that the app.min.css file is loaded instead of
     the app.css file. After this, there is one more thing that we can do before serving the CSS… and this
     is going to like you… a lot!
     Autoprefixer
     It’s possible that you already know this tool, but if you don’t, welcome to a new way to take
     advantage of your time when writing SCSS code. There exists a bunch of CSS features that, in
     order to make them work in every browswer, they need what is called vendor prefixes. An example
     will show you what I mean, this one makes a rounded corner:
     Chapter 6: Gulp (1)                                                                                  44
 1   -webkit-border-radius: 5px;
 2   -moz-border-radius: 5px;
 3   border-radius: 5px;
     There exist more vendor prefixes, the ones for Internet Explorer and Opera. Here you have the
     complete list:
     A lot of people solves this situation using Sass mixins, but the solution I suggest you is much better.
     What if I told you that you just have to write the CSS line without vendor prefixes? Following the
     previous example, just writing border-radius: 5px; should be enough, because Gulp will take care
     of prepending the needed CSS adding all the prefixes for us. In order to get this magic wand, add
     the following package:
Of course, we need to modify the Gulp task. Edit gulpfile.js with the new feature:
     Now, every time we launch gulp sass, we will transform SCSS into CSS, we will add the vendor
     prefixes to the app.css file, then we will minify this file and we will leave it like app.min.css. Isn’t
     it great? Just one more thing: nobody likes to be launching this task over and over again every time
     we edit a single SCSS file. Gulp solves this by default: the watch task.
    Chapter 6: Gulp (1)                                                                                 45
    Gulp watch
    Gulp comes with a default feature that works like this: “hey Gulp, every time I modify something
    in this path, launch this task”. Do you see where am I going to? What we are going to do is telling
    Gulp that every single time we edit a SCSS file it must call the previously created task. Edit the
    gulpfile.js file and add this code at the end of it:
1   gulp.task('watch', function () {
2       gulp.watch('./app/Resources/assets/scss/**/*.scss', ['sass']);
3   });
    In this task, a task that we called watch, of course, we are telling Gulp to be able to check for
    modifications of the “.scss” files withing the app/Resources/assets/scss folder and subfolders,
    and when it detects one change, call the sass task that we were using manually. Now, you just have
    to execute gulp watch in a terminal, edit a file and then save it, try it!
    Note: if you are using Ubuntu and this give you an error, try executing this command:
    Default task
    If you don’t give gulp any parameter, this means that if you just type gulp in a terminal, the default
    task will be executed. The most correct option is defining a task that can be interesting for our
    project. I usually launch the sass task and then start watching. This way, whenever I start developing,
    I just write gulp and off to go! Here I give you an example code, but feel free to modify this default
    task:
    With this last task, we’ve ended the first part of Gulp. In future chapters we are going to continue
    working with our gulpfile.js so it gives us a lot more features. Don’t forget that this is a didactical
    example, and you should check what others are doing with Gulp. For instance, you should read
    about postcss²⁵. As an exercise, you could try changing autoprefixer for cssnext.
      ²⁵https://github.com/postcss/postcss
Chapter 6: Gulp (1)                                                                                    46
Tip 6
Having our project well organized is vital for anyone implied in it. In this tip, I’m going to suggest
you one of the available options about organizing the SCSS files within a Symfony project, but I
want you to know that this is neither the unique one nor the best one option. Use it, check how you
feel working in this way and adapt it for you and for your projects.
First of all, you must know how Sass combine the files. You should have noticed that, when we
launched the Sass transformation with Gulp, it just created a single file. Sass will get all the files
from the folders that we tell him to check and it will generate a file for each single file that is not
starting with an underscore. Because of this, you should have an app.scss file and the rest of
the files should be included in this one, named as _file.scss. If you want to include a file in the
app.scss, you don’t need to write the underscore, neither the .scss.
After telling you this, well, writing this, let’s start talking about the folder structure. In the
app/Resources/assets/scss folder there should only live one single file: app.scss. The rest of the
files will be placed into new folders. The base folder will contain all the styles that will be applied
in a global way to all the platform. Here you have some examples:
    • _colors.scss: all the color variables that are going to be used in the project.
    • _common.scss: global styles, such as h1, a links, etc.
    • _fonts.scss: all the fonts to be included. If you also want to insert some base styles such as font
      sizing, you can rename this file to _typography.scss.
There are more files that can reside here, but I give you the chance to discover this as soon as you
develop new platforms. The next folder to talk about is components, in which we will hava a SCSS
file for each application component. But… what is a component? If you want to reuse something
between different pages, the best way to achieve this is by creating a component that can be included
wherever you want and a component that doesn’t depend with anyone more than itself: a carousel,
buttons, dropdowns, price tables, etc. Everyone of these will have a corresponding file in this folder.
The helpers folder will collect all the SCSS files that will help us with positioning or sizing elements.
For example:
    • _align.scss: even though this is comming with Bootstrap, it will help us to create alignment
      classes to the left, to the right, center and justified. Then we will be able to apply those classes
      to the elements and we won’t apply the same style over and over to an element.
    • _dividers.scss: this file will contain everything related to borders and other elements that
      divides components between them.
    • _icons.scss: to place and size the icons or images that are used in the correctly.
    • _spacing.scss: in order to include spacing in the elements without having to apply the concrete
      styling, just calling the mixins that are in this file (search on Google, is a very interesting
      topic).
Chapter 6: Gulp (1)                                                                                 47
The penultimate folder to comment out is layout. As you have guessed, this folder will contain at
least three files: the ones corresponding to the header, the content and the footer. Besides, you can
add new files to modify the styles of other elements for mobile or desktop.
The last one, the pages folder is where the specific page styles will reside. These styles are the ones
which don’t fit in the previously commented folders. The best example for this folder is the _404.scss
file, do you get it?
After this, we have finished this chapter’s trick and with it, one of the most interesting chapters in
the book. Do some tests and feel confortable with this stuff, you will work much quicker than you
previously did (and if this isn’t true, send me an email so I can learn how you work!)
Summary
In this chapter, we have learnt how to use Gulp tasks in order to tranform SCSS to CSS files. Besides,
we’ve created a watch task that will allow us to work directly with the files without having to launch
the precompile task over and over, automatically regenerating the resulting CSS file. In the next
chapter, we will be using a previously included bundle (until Symfony 2.7) called Assetic in order
to work with the JavaScript files, but you must know that you can also work with Gulp without a
problem.
    Chapter 7: Assetic for JavaScript
    It’s time to interact with the user, or at least the part that is related with the JavaScript of Bootstrap,
    the framework that we are using in our project. We could continue using Gulp in a similar way to
    the one that we did in the previous chapter for the Sass to CSS transformation. In fact, it’s something
    that I trully recommend. However, Assetic is a good option at this point because it’s very didactical
    and it came by default with Symfony until version 2.7.
    In this chapter, we are going to learn how to work with JavaScript files in an easy and correct way.
    In addition, in the trick, I will explain one of the possibilities to work with Symfony that I used to
    follow in my developments. As always, it’s not the unique one nor the best, it’s simply one that
    works well with me, and I hope it fits as well to you.
          NOTE: this is just a didactical chapter. The actual recommendation is using Gulp in
          order to work with JavaScript files. We will see this in the last chapter.
    Installation
    From Symfony 2.8, Assetic is not comming installed by default. So, if you are using this or a greater
    version, the first thing you must do is downloading the Composer dependency:
    With this done, you have to enable the bundle. Edit the app/AppKernel.php file and add the
    following instatiation line:
1 new Symfony\Bundle\AsseticBundle\AsseticBundle(),
1   assetic:
2       debug:           '%kernel.debug%'
3       use_controller: '%kernel.debug%'
4       filters:
5            cssrewrite: ~
     Configuration
     Before configuring anything, I would like to remember you that Assetic also works with Sass files,
     transforming them to CSS, but it’s too slow and it will make you lose a lot of time comparing with
     Gulp. Even so, I invite you to try it and tell me the results you obtain with both options or even any
     other possible alternative.
     The first thing we are going to do is downloading jQuery, just because Bootstrap needs it as a
     dependency or it won’t work correctly. In this case, Bootstrap 3 needs jQuery 1.x, a version that you
     can find on http://jquery.com/download/²⁶. You can download the uncompressed version because
     we will take care of minifying it at the end of the chapter. You must place the downloaded file in
     the web/js folder.
             NOTE: there exists some options here. We could use npm in order to download jQuery,
             but then we will have to create a Gulp task to copy the JavaScript file to the web public
             folder. We could use another technology such as Bower, which allows to download the
             dependencies in a specific configurable folder. We will see this later on.
     We already have the rest of the files available, so let’s start working. Edit the app/Resources/views/base.html.twig
     file and add the corresponding Assetic tag for JavaScript in the prepared MopaBootstrap base
     template:
 1   {% block head_script %}
 2       {% javascripts output='js/compressed/app.min.js'
 3           'js/jquery.js'
 4           'bundles/mopabootstrap/js/modernizr-2.7.1-respond-1.4.2.min.js'
 5           'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/toolt\
 6   ip.js'
 7           'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/*.js'
 8       %}
 9       <script type="text/javascript" src="{{ asset_url }}"></script>
10       {% endjavascripts %}
11   {% endblock head_script %}
     Check that the filenames are the same in your project. As you can see in the previous code
     block, the first thing we do is declaring the JavaScript outpput, which will be a file called
     js/compressed/app.min.js. For the time being, the file is not minified even though we added a
     .min to the filename, but we are preparing the road for the upcoming changes. The next lines include
     the different JavaScript files needed by out application. All these files will be combined in a single
     final file, taking care that the “popover” library needs “tooltip” to be loaded first. For this reason,
        ²⁶http://jquery.com/download/
    Chapter 7: Assetic for JavaScript                                                                   50
    we manually load the tooltip.js dependency first, because the asterisk loads the files in alphabetic
    order (I give you this tip for free, but you will have noticed because the browser console gives you
    this error). The intermediate line is just the HTML resulting line that the block will create.
    Time to test what we have done. Browse to http://127.0.0.1:8000/ and click the top dropdown. Is it
    working? I hope so, because if it’s not working, you will have to go back and check all the things
    we’ve done in the last 3 chapters.
    The next step is configuring the Twig filter that we will use in some minutes to minify our JavaScript.
    To configure this, edit the app/config/config.yml file and, in the assetic section, replace the content
    with the following block:
1   assetic:
2       debug:           "%kernel.debug%"
3       use_controller: false
4       bundles:         [ ]
5       #node: /usr/bin/nodejs
6       filters:
7            cssrewrite: ~
8            uglifyjs2:
9                bin: "%kernel.root_dir%/../node_modules/uglify-js/bin/uglifyjs"
    As you can see, the node binary is commented out. If your operative system has that binary in a
    different route (that is the default one), you have to tell Assetic where that binary is.
    The last step, modify the app/Resources/view/base.html.twig and add the recently configured
    filter to the javascripts section:
       ²⁷https://symfony.com/doc/current/cookbook/assetic/index.html
     Chapter 7: Assetic for JavaScript                                                                    51
 1   {% block head_script %}
 2       {% javascripts filter='uglifyjs2' output='js/compressed/app.min.js'
 3           'js/jquery.js'
 4           'bundles/mopabootstrap/js/modernizr-2.7.1-respond-1.4.2.min.js'
 5           'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/toolt\
 6   ip.js'
 7           'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/popov\
 8   er.js'
 9           'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/*.js'
10       %}
11       <script type="text/javascript" src="{{ asset_url }}"></script>
12       {% endjavascripts %}
13   {% endblock head_script %}
     We’ve done it! Refresh the page that we have been using as an example during the last chapters
     and you will see the JavaScript files minified automatically. But, be careful: this just happens when
     working in the development environment. This means that it just happens when browsing with
     app_dev.php (by default with the built in server). If you want to generate the minified final JavaScript
     file so everything works in the production environment, you should execute the following command
     in a terminal:
     Don’t you like having everything minified in the dev environment? Don’t panic, you can deactivate
     the filter just for the developent enviroment as it follows:
1 filter='?uglifyjs2'
     Just adding a “?” at the start of the filter makes Assetic ignore that filter when working in the
     development environment.
     With everything that we’ve covered during the last chapters, you can start working in a complete
     way with your project. For this reason, in the next chapter we will create a complete example of a
     Symfony page, from the routing declaration to the template rendering. But, just before going into
     this, I would like to recommend you some interesting related topics to this chapter.
     Recommendations
     I want to take advantage of this chapter section to talk about two topics that are, at least, advisable
     for reading. The first one is Assetic related. As you have seen, there are filters to minify CSS and
     JS, but there are also filters for image minification. Thank to jpegoptim, some malabarisms can be
    Chapter 7: Assetic for JavaScript                                                                52
    done for treating the images before being served by the web server. In this link²⁸ you can read a
    recipe that talks about this.
    On the other hand, we must continue talking about JavaScript. If your application is JavasCript
    intense, you should try to read about RequireJS: a JavaScript module loader file on demand with
    dependencies. Without any doubt your pages will get and after and a before using a technology like
    this, which is also compatible with almost every browser nowadays. Visit its webpage here²⁹ and
    start using it.
    Tip 7
    In this chapter’s tip I’m going to tell you how I used to work with my projects (not with every
    project) while I was developing them. As always, everyone has his/her own way of working and
    his/her own obsessions, tricks and defects, but thanks to this difference richness we have a sector
    that is growing as fast as light speed. If someone does ever tell you that his/her way of working is
    the best one, he/she is lying you, and that’s why I won’t tell you this.
    If you open the MopaBootstrap base template you will see a block like this:
1   {% block foot_script_assetic %}
2   {% endblock foot_script_assetic %}
    As the name designates, this block allows us to insert JavaScript elements before the footer. Even
    though the name contains the “assetic” work, we are obviously not forced to use this technology, so
    we will take advantage of this block to insert the JS code inside the same template.
    In every Twig file you work, you can add the following JS code that references the HTML that
    template file renders, for example:
1   {% block foot_script_assetic %}
2       <script>
3           $(document).ready(function() {
4                $('#form').submit(function() {
5                    console.log('Submit!');
6                });
7           });
8       </script>
9   {% endblock foot_script_assetic %}
       ²⁸https://symfony.com/doc/current/cookbook/assetic/jpeg_optimize.html
       ²⁹http://requirejs.org/
Chapter 7: Assetic for JavaScript                                                                 53
Logically, this code won’t get minified because it’s not placed into any JavaScript file, but it will
allow us to develop in an easier way because the JS code and the HTML page in which it should
work are just the same. When you finish your work and the version is more or less the final one,
remember to create the corresponding JavaScript file and copy the code you wrote in the template to
the new file. Moreover, you have to edit the %javacripts% Assetic block and add your own recently
created file that you should have placed in the web/js folder. When you finish, you should have
some JavaScript well organized files and a final app.min.js file with everything that our project
needs in order to work correctly.
Summary
In this chapter we have learnt to include the Bootstrap JavaScript necessary files and work with our
own JS files. At this point, we already have a solid base to start working in our Symfony project, so
in the next chapter we will be doing a complete example: from the routing to the rendering of the
template.
Chapter 8: full example
We have enough tools to start constructing our web project completely. Of course, we will learn
much more tools that will give us a lot of knowledgment to make our project more solid and
complete. but it’s time to stop a litte bit, consider what we have learnt until now and create a full
example to secure what we’ve seen in previous chapters.
In this chapter we will see how to work with Symfony since out app receives the “request” until we
give a “response” object. We will also use this chapter to talk about best practices in each section,
both the official ones and my personal ones.
Application flow
A lot of people say that Symfony as an MVC framework; others say that it’s a request-response
framework. I like the last description because it’s more like the way we are going to develop our
application.
In this chapter we are going to create a full homepage, with a carousel and some claims so we can
learn to work with the Bootstrap grid. In addition, we will create a contact form to work both with
Symfony forms and with services.
If you know exactly what you want to do in one of your projects, you must follow these steps:
1   home:
2       path:           /home
3       defaults:       { _controller: AppBundle:Home:index }
    Now, if you want to save some time and go directly to the controller code, you can achieve it
    thanks to annotations. Annotations allow us to declare a route in the same controller just over the
    function to be executed. This makes our code less decoupled but much easier to read.
    The same route as the previous one in the HomeController controller would be as this:
1   /**
2    * @Route("/home", name="home")
3    */
    You should also know that with annotations you don’t need to give a name to a route, it will
    automatically choose it’s name in this way: nameofbundle_nameofcontroller_nameofaction. In this
    case, the generated name would be app_home_index, easy, isn’t it? You can always see all your
    application routes with the following Symfony command:
1 bin/console debug:router
    In order to make annotations work, be sure that your controller is able to read annotations with the
    corresponding use in the top of the file:
1 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
We now have our first route working. Next step: the action method.
1        /**
2          * @Route("/home", name="home")
3          */
4        public function indexAction()
5        {
6
7        }
    At this moment, I won’t bother you with the PHP code styling, but you will have a future chapter
    that will take care of embarrash you. Talking about the controller, I must suppose that you know
    how to create one. Symfony comes with a DefaultController created for free, so you just have to
    copy it and rename it to HomeController, removing all the functions that you don’t really need.
    Continuing with our didactical example, the route and the action functions are ready, it’s time to
    start filling this method with PHP code.
        • Images carousel
        • Claims
        • Contact form
    I wanted our home to have these elements because each of them will use a distinct technique that
    will be didactically powerful.
    The image carousell will be using the Symfony repositories to get the images (featured, for example)
    and then the carousel component that Bootstrap provide us.
    We will make the claims static but using the component structure that we commented in chapter 6.
    We will use the Bootstrap grid and I will briefly explain how to make your content responsive.
    The contact form will use the Symfony forms and the services too. To make it a little more complex,
    we will insert it in a Bootstrap modal which we will be included by a controller call from a Twig
    template.
    You must understand and practice what I have showed you in this section because it’s basic for you
    to develop Symfony applications in a correct way. Don’t rush to the next section and create a solid
    base in your brain, it will be a lot easier to continue.
    In the last part of this section, I would like to talk to you about “controllers”. A lot of people think that
    controllers are a hodgepodges in where you can write a bunch of PHP lines (a lot) that then become
    unmaintainable and, of course, impossible to reuse. We have to avoid this at all costs, because doing
    it the right way won’t cost you a lot of time in a short term period and it will save a lot of time in
    the future. Let’s start with the image carousel.
    Chapter 8: full example                                                                                 57
1 bin/console doctrine:generate:entity
The prompt will ask you some questions, this is what I wrote:
    While answering the second question, you can also use any other of the suggested formats. It’s
    very similar to the routing system because, if you use YAML, for example, it will create you a
    [bundle]/Resources/config/doctrine/FeaturedImage.orm.yml file where all configuration will
    be placed. For this example, like in my own projects, we will be using annotations.
    You now can see another two new files in your bundle:
        • src/AppBundle/Entity/FeaturedImage.php
        • src/AppBundle/Repository/FeaturedImageRepository.php
    We will place all the database queries related to the FeaturedImage entity in the FeaturedIm-
    ageRepository file. Wait some minutes and we will be using this feature.
    To edit the database structure, remember that you should execute the following command in a
    terminal:
    If you are not sure what is going to happen when executing it, you can change the parameter --force
    for --dump-sql and see what queries will be executed before launching the database changes.
    Next step: create the admin class. Here is my src/AppBundle/Admin/FeaturedImageAdmin.php
    source code:
     Chapter 8: full example                                                             58
 1   <?php
 2
 3   namespace AppBundle\Admin;
 4
 5   use   Sonata\AdminBundle\Admin\AbstractAdmin;
 6   use   Sonata\AdminBundle\Datagrid\ListMapper;
 7   use   Sonata\AdminBundle\Datagrid\DatagridMapper;
 8   use   Sonata\AdminBundle\Form\FormMapper;
 9
10   class FeaturedImageAdmin extends AbstractAdmin
11   {
12       /**
13         * {@inheritdoc}
14         */
15       protected function configureFormFields(FormMapper $formMapper)
16       {
17            $formMapper
18                ->add('url', 'url')
19                ->add('active')
20            ;
21       }
22
23         /**
24           * {@inheritdoc}
25           */
26         protected function configureDatagridFilters(DatagridMapper $datagridMapper)
27         {
28              $datagridMapper
29                  ->add('url')
30                  ->add('active')
31              ;
32         }
33
34         /**
35           * {@inheritdoc}
36           */
37         protected function configureListFields(ListMapper $listMapper)
38         {
39              $listMapper
40                  ->addIdentifier('id')
41                  ->add('url')
42                  ->add('active', null, array('editable' => true))
     Chapter 8: full example                                                                                                    59
I’ve added a couple of tricks more to this admin that were not present in previous chapters:
           • In the form, I’ve added a second parameter to the “url” field, which corresponds to the “input
             type”. With this parameter, we make sure that what it’s going to be typed in the form will be
             a URL. You should also read the validation³⁰ chapter in the Symfony documentation.
           • In the list section, Sonata allows an “editable” parameter that will allow to edit boolean fields
             from the list view with just a click, without going to the form.
 1   sonata.admin.featured_image:
 2       class: AppBundle\Admin\FeaturedImageAdmin
 3       tags:
 4           - { name: sonata.admin, manager_type: orm, group: "Content", label: "Fea\
 5   tured Image" }
 6       arguments: [~, AppBundle\Entity\FeaturedImage, ~]
     It’s time to add some images. For this example, we will search on Google for images with this size:
     1280x550. Here you have a search link³¹ with some images so you can insert some of them into the
     database.
     Ok, we’ve inserted some images, so our next step will be “retrieving” those images from the database.
     In order to achieve this goal, we must create a new function inside the FeaturedImageReposi-
     tory.php file that will contain the carousel needed images. It’s possible that in a future day the
     images appearing in the carousel follow a distinct algorithm, and because of that, the function
     must be called something like “findCarouselImages” instead of “findLastActiveImages” (with this
     statement I’m trying to tell you that it’s possible that in the future, the images that will be appearing
     in the carousel could be the last active images uploaded between date A and B, and it’s easier to keep
     the same function name and edit the algorithm instead of changing the function name everywhere).
     Edit src/AppBundle/Repository/FeaturedImageRepository.php and add the function that fit your
     needs, for example:
         ³⁰https://symfony.com/doc/current/book/validation.html
         ³¹https://www.google.es/search?q=landscape&espv=2&biw=1680&bih=913&tbs=isz:ex,iszw:1280,iszh:550&tbm=isch&source=lnt
     Chapter 8: full example                                                                               60
 1   <?php
 2
 3   namespace AppBundle\Repository;
 4
 5   use Doctrine\ORM\EntityRepository;
 6
 7   /**
 8     * FeaturedImageRepository
 9     *
10     * This class was generated by the Doctrine ORM. Add your own custom
11     * repository methods below.
12     */
13   class FeaturedImageRepository extends EntityRepository
14   {
15        public function findCarouselImages()
16        {
17            return $this->createQueryBuilder('f')
18                ->where('f.active = :active')
19                ->setParameter('active', true)
20                ->setMaxResults(5)
21                ->getQuery()
22                ->getResult();
23        }
24
25   }
     Caution:: repository classes are intermediates between code and database, so do not use repository
     files to create business logic. This must be placed in a service if it can be reused o in a controller if
     not. The previous repository function is an example, and it could have been done with the default
     repository like this:
     Finally, our src/AppBundle/HomeController.php should call the recently created function to obtain
     the images and then pass the variable to the corresponding Twig template. Here you have the source
     code:
     Chapter 8: full example                                                                                61
 1   <?php
 2
 3   namespace AppBundle\Controller;
 4
 5   use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 6   use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 7
 8   class HomeController extends Controller
 9   {
10       /**
11         * @Route("/home", name="home")
12         */
13       public function indexAction()
14       {
15            $em = $this->getDoctrine()->getManager();
16            $fimageRepository = $em->getRepository("AppBundle:FeaturedImage");
17            $featuredImages = $fimageRepository->findCarouselImages();
18
19             return $this->render('home/index.html.twig', array(
20                 'featuredImages' => $featuredImages
21             ));
22        }
23   }
     We are almost at the end. The next step: create the app/Resources/views/home/index.html.twig
     template, make it extend our base template and fill the correct block.
 1   {% extends 'base.html.twig' %}
 2
 3   {% block container %}
 4       <div class="container">
 5           <h1>Here goes the carousel</h1>
 6       </div>
 7   {% endblock %}
     Hey, what have you done here? So it results that our base template contains a heading, a sidebar
     and a footer that we absolutely don’t need them for the homepage. I’ve just searched for another
     block above them to override the part that I want. In this case, the container block fits quite good
     and allows us to override the full template.
     It’s possible that you already have noticed of an error and you have tried to solve the problem of the
     topbar. As it is a fixed element, it doesn’t fill any space at all, and because of that, our page starting
     point is covered by that bar. If you read the Bootstrap documentation you will find the suggested
     fix. Create an app/Resources/assets/scss/base/_common.scss file with the following content:
     Chapter 8: full example                                                                          62
 1   body {
 2     padding-top: 55px;
 3   }
And of course, edit the app/Resources/assets/scss/app.scss and include the created file:
 1   @import '../../../../web/bundles/mopabootstrap/sass/mopabootstrapbundle-3.2';
 2   @import 'base/common';
     Also rembeber that, if you don’t have gulp watch running, you must execute the Gulp command
     that transforms our SCSS files to CSS. One you finish this, check http://127.0.0.1:8000/home and see
     how it looks like.
     To end this section, we have to show our carousel. You have the Bootstrap code in this link³². Copy
     and paste it in your Twig template, and try to adapt it to show our featured images coming from the
     database. If you don’t accomplish this, here you have the solution:
 1   {% extends 'base.html.twig' %}
 2
 3   {% block container %}
 4       <div class="container">
 5           <div id="carousel-example-generic" class="carousel slide" data-ride="car\
 6   ousel">
 7               <ol class="carousel-indicators">
 8                   {% for i in 1..featuredImages|length %}
 9                       <li data-target="#carousel-example-generic" data-slide-to="{\
10   { i-1 }}"{% if loop.first %} class="active"{% endif %}></li>
11                   {% endfor %}
12               </ol>
13               <div class="carousel-inner" role="listbox">
14                   {% for featuredImage in featuredImages %}
15                       <div class="item{% if loop.first %} active{% endif %}">
16                           <img src="{{ featuredImage.url }}" alt="">
17                           <div class="carousel-caption">
18                                Image caption {{ loop.index }}
19                           </div>
20                       </div>
21                   {% endfor %}
22               </div>
23
24                      <!-- Controls -->
       ³²http://getbootstrap.com/javascript/#carousel
     Chapter 8: full example                                                                         63
     Impressive! With this first example we’ve learnt how to create a route and a function, create an
     entity with a repository to retrieve the needed data, send the proper information to the template
     and render the Bootstrap carousel element. If you want to improve this part, you could encapsulate
     the Twig part in a reusable component… but we will learn how to do this in the next section of the
     chapter.
     The claims
     With this section we will learn how to master (a little bit) the Bootstrap grid and we will also
     manage our page with components. This is not a Bootstrap book, so I will rush when talking about
     some of its elements. It’s your responsability to learn how to correctly use Bootstrap.
     To be more agile, we won’t retrieve the claims from the database; instead, we will be creating them
     directly from the previous used template and, before closing the main “div”, we must insert three
     claims as it follows:
     As you can see, we’ve created a separate component in a different template so this component can
     be included anywhere in our application. The claim code is also from the Bootstrap examples. I’ve
     adapted it a little bit, but you can use whatever you like (app/Resources/views/component/claim.html.twig):
     Chapter 8: full example                                                                                64
Let’s stop here a second and let me explain you briefly how the grid system works:
           • In order to insert columns (all classes starting with col-) you must first create a row class tag.
           • Being a mobile first solution, you should write the most concrete classes at the begining and
             then the less concrete ones (from col-xs- to col-lg-).
     You can read about all the available classes in this link³³.
     Refresh the page to see the result, how does it look like? I also think that the webpage needs more “col-
     ors”, so let’s create the corresponding SCSS app/Resources/assets/scss/component/_claim.scss
     file:
 1   .claim {
 2     margin: 10px 0;
 3
 4       &__title {
 5         color: #f22;
 6       }
 7
 8       &__text {
 9         font-style: italic;
10       }
11   }
     As homework, you should remove that hexadecimal color from there and create the corresponding
     variable in the _colors.scss file containing that value, and then use it. Be careful! Don’t forget to
     update your app/Resources/assets/scss/app.scss file. Refresh again and see the changes.
     Do you see the same goal as me? We’ve made a perfectly reusable component to use anywhere, and
     if we had a style guide with the corporative colors, we just simply have to assign color variables to
         ³³http://getbootstrap.com/css/#grid-options
     Chapter 8: full example                                                                          65
     the components instead of repeating hexadecimal values over and over. Because… what happens if
     the corporative colors change? You should use the “search and replace” feature in the whole project.
     But, if you are using Sass, you just change the variable values and, that’s it!
     Now, it’s time to start with something a bit comre complicated: forms and services.
     Contact form
     The first thing we are going to do in this seacton is creating the Symfony form. It’s very advisable
     that you master this chapter³⁴ of the Symfony book because the forms’ power in Symfony is just
     awesome. On the other had, MopaBootstrap has an easy way of rendering forms that you can read
     here³⁵. In this example, we are going to render our form manually, but improving this part would
     be a good homework.
     Firstly, we have to create our form file src/AppBundle/Form/Type/ContactType.php. Here you have
     the source code that we will be using for the example:
 1   <?php
 2
 3   namespace AppBundle\Form\Type;
 4
 5   use   Symfony\Component\Form\AbstractType;
 6   use   Symfony\Component\Form\FormBuilderInterface;
 7   use   Symfony\Component\OptionsResolver\OptionsResolver;
 8   use   Symfony\Component\Validator\Constraints\Collection;
 9   use   Symfony\Component\Validator\Constraints\NotBlank;
10   use   Symfony\Component\Validator\Constraints\Length;
11   use   Symfony\Component\Validator\Constraints\Email;
12
13   class ContactType extends AbstractType
14   {
15       public function buildForm(FormBuilderInterface $builder, array $options)
16       {
17           $builder
18               ->add('name', 'text', array(
19                   'label' => 'Nombre',
20                   'attr' => array(
21                       'placeholder' => 'Introduce tu nombre',
22                       'autocomplete' => 'off'
23                   )
       ³⁴https://symfony.com/doc/current/forms.html
       ³⁵http://bootstrap.mohrenweiserpartner.de/mopa/bootstrap/forms/examples
     Chapter 8: full example                                                                 66
24                   ))
25                   ->add('email', 'email', array(
26                       'attr' => array(
27                           'placeholder' => 'Para contactar contigo',
28                           'autocomplete' => 'off'
29                       )
30                   ))
31                   ->add('message', 'textarea', array(
32                       'label' => 'Mensaje',
33                       'attr' => array(
34                           'cols' => 90,
35                           'rows' => 10,
36                           'placeholder' => '¿Qué necesitas saber?'
37                       )
38                   ))
39             ;
40        }
41
42        public function configureOptions(OptionsResolver $resolver)
43        {
44            $collectionConstraint = new Collection(array(
45                'name' => array(
46                    new NotBlank(array('message' => 'El nombre no puede estar vacío'\
47   ))
48                   ),
49                   'email' => array(
50                       new NotBlank(array('message' => 'El email no puede estar vacío')\
51   ),
52                        new Email(array('message' => 'Email incorrecto'))
53                   ),
54                   'message' => array(
55                       new NotBlank(array('message' => 'El mensaje debe ser más largo')\
56   ),
57                        new Length(array('min' => 5))
58                   )
59             ));
60
61             $resolver->setDefaults(array(
62                 'constraints' => $collectionConstraint
63             ));
64        }
65
     Chapter 8: full example                                                                           67
 1   <?php
 2
 3   namespace AppBundle\Controller;
 4
 5   use   Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 6   use   Symfony\Bundle\FrameworkBundle\Controller\Controller;
 7   use   Symfony\Component\HttpFoundation\Request;
 8   use   AppBundle\Form\Type\ContactType;
 9
10   class ContactController extends Controller
11   {
12       /**
13         * @Route("/contact")
14         */
15       public function contactAction(Request $request)
16       {
17            $form = $this->createForm(new ContactType());
18            $form->handleRequest($request);
19            if ($form->isSubmitted() && $form->isValid()) {
20                // Form sent
21                die;
22            }
23
24             return $this->render('contact/contact.html.twig', array(
25                 'contactform' => $form->createView()
26             ));
27         }
28   }
     You can read in the previous source code that, at this moment, we are not doing anything when the
     form is submitted. If the form hasn’t been submitted, we will render a template with the contact
     Chapter 8: full example                                                                               68
     form. Be careful, if you created the controller from scratch check the use that I wrote in the top part
     of the file.
     The template that we are going to use will receive the contact form that we must render as beautiful
     as possible for our users. Let’s go!
 1   {# app/Resources/views/contact/contact.html.twig #}
 2   {% extends 'base.html.twig' %}
 3
 4   {% block content_content %}
 5       {{ include('component/contact_form.html.twig') }}
 6   {% endblock %}
     Another component! In further sections we will be embedding this form in a Bootstrap modal, so it’s
     a good idea to have everything as a component, it will allow us to reuse the code without repeating
     it. The contact_form.html.twig template looks like this:
     Wow! Is that simple? Definitely, it is very simple. But, if you see the first line of this template file,
     you will see that we are rendering the form with Bootstrap classing. You must enable this feature in
     your app/config/config.yml file:
 1   twig:
 2       debug:            "%kernel.debug%"
 3       strict_variables: "%kernel.debug%"
 4       form:
 5           resources: ['bootstrap_3_layout.html.twig']
     You can read about Bootstrap theming in this link³⁶, and here³⁷ you have all about form customiza-
     tion.
       ³⁶http://symfony.com/blog/new-in-symfony-2-6-bootstrap-form-theme
       ³⁷http://symfony.com/doc/current/cookbook/form/form_customization.html
     Chapter 8: full example                                                                           69
     The form is almost working, but we are still missing a couple of things: - Make our form appear in
     a Bootstrap modal - Send the needed email, indeed!
     For the first goal, you must need to know how Bootstrap modals work, something that you can
     read in the documentation³⁸. Did you read it? So let’s continue with the example.
     The idea here is inserting a footer link that shows “contact”, but instead of redirecting you to the
     contact page, it will popup a modal with the created form. Logically, in order to render the form in
     every page (it is in the footer), every time the base template gets loaded, the contactform variable
     must exist. This can be done in two ways:
         • Create the form in every action of you project, something that is bad
         • Make your base template call an action that will render that modal with the form
     You agree with me, let’s stick with the second option. We have to create the modal as a component
     so it can be reused in any other part of the application. My app/Resources/views/component/-
     modal.html.twig is this one:
     I simplified a bit the Bootstrap example but you can improve it as much as you want. This component
     receives three different variables: the modal identifier, in order to open it wherever we want; the
     modal title; and the modal content. The last variable use the “raw” filter because it can contain
     HTML text. With this filter, it won’t be escaped.
     The next step is modifying our ContactController in order to renderize the modal when it comes
     from a subrequest (a template that called a controller) or render the page if it’s not. Here you have
     the needed modifications.
       ³⁸http://getbootstrap.com/javascript/#modals
     Chapter 8: full example                                                                              70
 1   /**
 2    * @Route("/contact")
 3    */
 4   public function contactAction(Request $request) {
 5       $form = $this->createForm(new ContactType(), null, array(
 6           'action' => $this->generateUrl('app_contact_contact'),
 7       ));
 8       $form->handleRequest($request);
 9       if ($form->isSubmitted() && $form->isValid()) {
10           // Form sent
11           die;
12       }
13
14        $requestStack = $this->get('request_stack')->getParentRequest();
15        if ($requestStack) {
16            $modalContent = $this->renderView('component/contact_form.html.twig', [
17                'contactform' => $form->createView()
18            ]);
19
20             return $this->render('component/modal.html.twig', array(
21                 'identifier' => 'contactModal',
22                 'title' => 'Contact Form',
23                 'content' => $modalContent
24             ));
25        }
26
27        return $this->render('contact/contact.html.twig', array(
28            'contactform' => $form->createView()
29        ));
30   }
     As you can see, I did a small trick to know if the process comes from a parent request thanks to
     the “request_stack” service. This will allow us to identify what it has to be returned as a response.
     In case of being a subrequest, we will first load the content of the form in a variable thanks to the
     renderView method, instead of the render one which returns a Response object. The renderView
     returns just a string. I also added the action parameter to force the form POST to the same controller
     function, always using Symfony routes.
     The last step is editing our base.html.twig and add to it the followig code in the footer section:
     Chapter 8: full example                                                                            71
 1   {% block footer %}
 2   <p>Footer sexy 2015 - <a href="javascript:void(0)" data-toggle="modal" data-targ\
 3   et="#contactModal">Contact</a></p>
 4   {% endblock footer %}
 5
 6   {% block body_end %}
 7       {{ render(controller('AppBundle:Contact:contact')) }}
 8   {% endblock body_end %}
     In that base template block, just before closing the body tag, the modal with our form will be loaded
     in every single page. Do you want to try it? Refresh the page and click on the contact link in the
     footer.
     The view part is over. It’s time to give some back functionality to our contact form.
     Sending an email
     In this seciton we are going to learn two different things: sending an email (this was the easy one to
     guess) and creating and using a Symfony service.
     The first thing we are going to do is creating the service that will allow to send emails from our
     application. It’s very similar to a controller, I’m sure you won’t have any problems to understand
     what I show you in the following code block. This is the code for the src/AppBundle/Service/App-
     Mailer.php file that will work for now:
 1   <?php
 2
 3   namespace AppBundle\Service;
 4
 5   class AppMailer {
 6
 7        private $mailer;
 8
 9        public function __construct($_mailer) {
10            $this->mailer = $_mailer;
11        }
12
13        public function sendEmail($to, $subject, $text) {
14            $message = \Swift_Message::newInstance()
15                ->setSubject($subject)
16                ->setFrom('noreply@domain.com')
17                ->setTo($to)
     Chapter 8: full example                                                                            72
18                 ->setBody($text)
19             ;
20             return $this->mailer->send($message);
21        }
22
23   }
     As you can see, we have a constructor which receives services to use inside this class, and a function
     to send en email according to the received parameters. Swift Mailer already comes with Symfony,
     so do not bother about it.
     The next step is declaring the service. We just have to write the class and the arguments that the
     constructor will receive. We must add the following code in our services.yml:
 1   appmailer:
 2       class: AppBundle\Service\AppMailer
 3       arguments: ["@mailer"]
     If you want to test it, you can edit your app/config/config_dev.yml file and add your Gmail acount
     this way:
    Chapter 8: full example                                                                            73
1   swiftmailer:
2       transport: gmail
3       username: your_gmail_username
4       password: your_gmail_password
    As homework, you must improve the sent email by creating something a bit more sophisticated with
    HTML thanks to the Twig template engine. You have the Symfony email documentation here³⁹.
    With this, we have finished a complete Symfony example in which we have seen almost every “base
    column” of the framework, and with that, you became a Symfony jedi. Even so, I have to tell you that
    after several years working with this framework, I’m still learning nowadays, and that is wonderful.
    Tip 8
    It’s possible that sometimes you need to render a page without database code and without business
    logic. This means that the controller function will have a single line to render the template. In this
    case, Symfony allows us to render a template without having a controller. In order to do so, you
    need an special route that cannot be done with annotations. Edit your routing.yml file and add
    the following YAML route to it:
1   privacy_policy:
2       path: /privacy
3       defaults:
4           _controller: FrameworkBundle:Template:template
5           template:    static/privacy.html.twig
    Is this simple, we have avoided to create some lines of code that we just needed to load a template.
    But if you are an advanded Symfony user, you will have noticed that there exists one problem: how
    can I cache the content now without a Controller? Calm down, there is a solution for that:
1   privacy_policy:
2       path: /privacy
3       defaults:
4           _controller:             FrameworkBundle:Template:template
5           template:                static/privacy.html.twig
6           maxAge:                  86400
7           sharedAge:               86400
      ³⁹http://symfony.com/doc/current/email.html
    Chapter 8: full example                                                                          74
    As this chapter has been very long, I will give you another tip. Imagine that you migrate some or
    your URLs and, because of the SEO, you need to do some redirections. You can achieve this in several
    ways without Symfony, but just because this book is not about Apache or others, I will explain you
    how is this done in Symfony. Like in the previous example, you can catch the old route with a
    controler and return a “return $this->redirect('...')”. However, we can also do the same with
    YAML:
1   wpadmin:
2       path: /wp-admin
3       defaults:
4            _controller: FrameworkBundle:Redirect:redirect
5            route: sonata_admin_dashboard
6            permanent: true
    With the previous route, we created a ficticious WordPress migration (wp-admin) to Symfony. Now
    the users that browse to /wp-admin will be redirected to our Sonata made admin.
    Summary
    In this chapter, we have created a very complete example of several elements from Symfony and
    Bootstrap. We have learnt to create new routes, new repositories for whatever it is related to
    database, we have created services to decouple reusable functionality, we have improved our Twig
    knowledgement, its templates and some other elements that are fontend related (Bootstrap based).
    In the next chapter we will see other tools that will maximize our development even more.
    Chapter 9: Doctrine Extensions
    This is the first chapter in where we start talking about bundles you can use to develop and improve
    your projects without being part of the base project we are building. I wanted to expend a full chapter
    to a very interesting bundle: Doctrine Extensions. With this “plugin” you will be able to “vitaminize”
    your entities with extra features that are normally repeated between projects. Storing the last time
    an entity was edited, calculate the slug with a specific function or even more complex features like
    field translation or version saving are automatically done with this bundle. As always, let’s start
    with the installation.
    Installation
    If you search the name of this bundle as it is, you will see that it exists and you can install it.
    However, you will have to create the filter associated services for your own. Most of them are
    relatively simple for the 99% of the cases, and that’s why STOF created a bundle over Doctrine
    Extensions with everything you need to start using those filters.
    First of all, you must add the needed line to our dependency definition file composer.json:
Then, you must enable the bundle in the app/AppKernel.php file in order to start using it:
1 new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
    Almost everything is done to start using it… but we missed the configuration part. This bundle is
    a bit special, so we will be configuring the necessary parts while using it. In the next sections, I
    will explain you how three of its filters are used, the rest of them are up to you (even though I will
    explain them) and you will see if they fit your future project needs or they don’t.
    Timestampable filter
    We will start by one of the easiest filters of the bundle. The first thing we must know is what this
    filter does. If you read the filter documentation, which is linked at the end of this section, you will
    see that this filters allows to establish a date in a concrete field of the entity whenever:
        • It is created
     Chapter 9: Doctrine Extensions                                                                   76
         • It is updated
         • Some or one of its fields get updated
         • A field get updated to a concrete value
     We have to configure the filter to start using it. So, let’s edit the app/config/config.yml file and
     add a new configuration block:
 1   stof_doctrine_extensions:
 2       orm:
 3            default:
 4                timestampable: true
     From now on, we just have to add the new filters configuration to this block. I like to keep them
     alphabetically ordered, but feel free to keep them the way you like more.
     Filter activated, it’s time to use it. Edit the product entity and make it save whenever it gets
     created, updated and when a product name changes. The following code is what I added in the
     src/AppBundle/Entity/Product.php file just before the getters and the setters:
     Did you understand what I’ve done? The first thing is adding the “use” in the top part of the file
     that will allow us to write @Gedmo annotations. Then we have created a datetime variable for each
     needed option. Remember to create the getters and the setters for this entity:
And, indeed, as we added new columns, we have to update our database schema:
     Everything prepared for the action, except for a small detail: how can we see if it’s working? There
     are several ways, but just because we already built an amazing admin, we are going to use it now.
     Edit the src/AppBundle/Admin/ProductAdmin.php file and add the following to the list function:
     In order to test it, browse to http://127.0.0.1:8000/admin and create a new product, edit it in various
     ways and see how the fields are getting updated.
     This filter documentation is available in this link⁴⁰. But I’m going to give a small tip a little earlier:
     Symfony does have a tool to edit entity values before and after some events happen. This feature is
     called “Lifecycle Callbacks”, and you should read about how it works in this link⁴¹ before you get
     confortable with Gedmo.
       ⁴⁰https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/timestampable.md#timestampable-entity-example
       ⁴¹http://symfony.com/doc/current/book/doctrine.html#lifecycle-callbacks
    Chapter 9: Doctrine Extensions                                                                   78
    Slugabble filter
    With this filter you will be able to create your own slugs just as easily as in the date update of
    the previous example. If you don’t know what a slug is, let me explain it with an example. If
    you have a product in your database called “My hello product”, the best URL to be considered as
    “SEO friendly” would be something like /products/my-hello-product. This string without spaces,
    lowercase, without accents and without special characters is called slug.
    Let’s enable the filter to see it working. Edit the configuration file app/config/config.yml and add
    the new filter:
1   stof_doctrine_extensions:
2       orm:
3            default:
4                sluggable: true
5                timestampable: true
Then, edit the product entity and add the slug field just after the name, to keep it in order:
1   /**
2    * @Gedmo\Slug(fields={"name"})
3    * @ORM\Column(length=105, unique=true)
4    */
5   private $slug;
    Generate the getters and the setters with the same command we used previously. To keep the entity
    in order, just as with variables, you should order the generated methods. You can delete all of them
    manually and regenerate them again, or once generated cut and paste them in the place they should
    be, but I suggest you to keep them in the same order as the variables:
    Finally, you must update the database but first you must delete all previously created products
    because the slug is a unique field and, when you update the schema, all products will have the same
    empty slug and and error will be launched: “duplicate entry”. You can delete them from the admin.
    Once you finish, execute the following database schema update command from a terminal:
    Let’s see it up and running. Again, edit the ProductAdmin class and add the new created field to the
    list action:
     Chapter 9: Doctrine Extensions                                                                        79
     Create a new product and see it the slug was generated, do you like it? If you look carefully, when
     you update the entity name the slug also gets updated. Thinking about website in general, this could
     produce a 404 error if the URL has already been shared, for example, in social networks. If you want
     to avoid this, there exists an option to deactivate the slug update when an entity is modified. This
     and a bunch of other possible configurations may be obtained adding the following parameters to
     the annotation recently created:
    Everything went OK until you read the “handlers” list item. What are they? In most of your projects
    you won’t be using this feature, but it can be very helpful.
    There are several default handlers that will allow you to generate slugs in a more detailed way. For
    example: if you have the product entity related to a category, it’s possible that you like to have a
    slug like “/category/product”. Would you like to do an example? Ok, you will do the next exercise
    with this documentation⁴².
    On the other hand, you have the sluggable filter documentation in this link⁴³.
    Softdeleteable filter
    How many times did you hear that “you must not remove database rows, just mark them as deleted”?
    With this filter we will be able to achieve that goal without having to trouble with all the queries
    you already developed, because this filter does everything by default. Do you understand this? Ok,
    you will do in a minute.
    The first thing is enabling the filter. Edit the configuration file app/config/config.yml:
1   stof_doctrine_extensions:
2       orm:
3            default:
4                sluggable: true
5                softdeleteable: true
6                timestampable: true
    With this configuration, we will mark an entity as deleted when removing it. But we also want to
    retrieve all not-as-deleted products when quering “give me all the database products”. To get this
    done, you must also add an orm filter. Here you have the configuration block. You only have to add
    the filters stuff, the other part is already created. Search for it in the app/config/config.yml file:
1   doctrine:
2       orm:
3            filters:
4                softdeleteable:
5                     class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
6                     enabled: true
    Now that the filter is configured, we must add a new property to our product entity in order to save
    whenever it gets deleted:
      ⁴²https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md#some-other-configuration-options-for-slug-annotation
      ⁴³https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md
     Chapter 9: Doctrine Extensions                                                                          81
 1   <?php
 2
 3   namespace AppBundle\Entity;
 4
 5   use Gedmo\Mapping\Annotation as Gedmo;
 6   use Doctrine\ORM\Mapping as ORM;
 7
 8   /**
 9     * @ORM\Entity
10     * @ORM\Table(name="product")
11     * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
12     */
13   class Product
14   {
15        //...
16
17        /**
18         * @ORM\Column(name="deleted_at", type="datetime", nullable=true)
19         */
20        private $deletedAt;
21   }
     Did you see that? This annotation is a bit different, and it’s in an entity level. This will allow Doctrine
     to know when to return an entity and how to set the deleted value in the database. As always,
     generate the getters and the setters and update the database schema:
     You can browse to the admin section and delete one of your products. When you do it, you won’t
     be able to see it in the admin but, if you browse your database with PhpMyAdmin or any other
     software you like, you will see that it is still there. This is because of the second configuration that
     we added. So, if you execute the following code, you will see that the deleted products are not
     being returned:
1 $products = $em->getRepository("AppBundle:Product")->findAll();
     If you need all products for whatever reason, even the deleted ones, you can deactivate the orm filter
     like this:
    Chapter 9: Doctrine Extensions                                                                        82
1   $em->getFilters()->disable('softdeleteable');
2   $products = $em->getRepository("AppBundle:Product")->findAll();
    Other filters
    There are a lot of filters that you can use thanks to this bundle. You will find how to configure and
    use them in this link⁴⁵. I strongly recommend you to read all the filters options to see how much
    code you can save thanks to others’ work. But, to finish with good sensations, here you have the
    rest of the filters briefly explained:
        • blameable: just as the timestampable** filter it allow you to set an entity field when an
          event happens but with a *string instead of a datetime. If it’s in a text field, it will insert the
          username; if it’s a relation, is will create the conection between the entities. Documentation⁴⁶.
        • iptraceable: same as the previous point, but it just sets the user IP as a string. Documentation⁴⁷.
        • loggable: tracks the entity changes and allows to manage its versions. Documentation⁴⁸.
        • sortable: it keeps a position field to order the rows. Documentation⁴⁹.
        • translatable: it allows to insert specific field for different languages, automatically loading
          the concrete user translation when it browses the page. Documentation⁵⁰.
        • tree it allows to implement the Nested-Set behaviour in an entity, supporting several
          strategies. Documentation⁵¹.
        • uploadable it allows go manage the file persistence with Doctrine such as moving, renaming
          or removing those files automatically. Documentation⁵².
    Tip 9
    In this chapter we have seen how the slugs work. I’m sure you already experienced or you are about
    to experience that a lot of routes will start the code as “give me the object whose slug is this one”.
    Symfony developers saw this code repetition and they created a way of jumping over this step. To
    do so, you just have to edit the action declaration so it receives the object and not the slug string.
    Here you have an example:
      ⁴⁴https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/softdeleteable.md
      ⁴⁵https://github.com/stof/StofDoctrineExtensionsBundle/blob/master/Resources/doc/index.rst
      ⁴⁶https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/blameable.md
      ⁴⁷https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/ip_traceable.md
      ⁴⁸https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/loggable.md
      ⁴⁹https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sortable.md
      ⁵⁰https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/translatable.md
      ⁵¹https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/tree.md
      ⁵²https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/uploadable.md
    Chapter 9: Doctrine Extensions                                                                    83
1   /**
2     * @Route("/products/{slug}")
3     */
4   public function indexAction(Product $product)
5   {
6        // ...
7   }
    As you see, instead of receiving the slug directly I’m telling Symfony to query the database for me.
    Ah, another thing! If the product doesn’t exist, it will automatically launch a 404 exception without
    you having to do it manually.
    How many lines of code have I just saved you? This is nothing at all. This technique is called
    ParamConverter and we are going to use it a lot deeper in chapter 13, just because with some simple
    (well, not always so simple) annotations, we will have our entity objects ready just before starting
    our action code.
    Summary
    In this chapter we have learnt to add several improvements to our entities thanks to the Doctrine
    filters that give us a basic functionality but they got repeated over and over again in our projects.
    Thanks to some simple annotations and a very small configuration, we have given those features to
    our project and we have removed (or saved) lots of code lines.
    In the next chapter we will be moving to something a bit more visual and we will be working with
    a bundle that allows us to manage images before serving them to the users.
     Chapter 10: LiipImagineBundle
     This chapter may be a little shorter than the previous ones, but I would like to give it a whole chapter
     to the image processing. If you search over Internet you will find some bundles that will allow you
     to manage images with Symfony, but after testing some of them personally, the one that best works
     in my own opinion is LiipImagineBundle. In this chapter I will explain you how to install the bundle,
     configure it and work with it. Let’s start!
     Installation
     The installation is as simple as executing the following command:
     Finally, you have to add some routes to the app/config/routing.yml file because without them,
     our filters won’t work:
 1   _liip_imagine:
 2       resource: "@LiipImagineBundle/Resources/config/routing.xml"
     With this done, everything is correct but… what are those filters? The filters are just bundle
     configuration blocks that will allow us to apply some transformations to the images, both with
     the Twig filter (do not confuse yourself with the filter concept of both topics) and applying the filter
     to the image from our PHP code. In the next section I will explain how this works.
     Chapter 10: LiipImagineBundle                                                                     85
     Configuration
     By default, this bundle creates the modified images in the web/media/cache folder, so we have to
     allow our webserver to write on this folder. Here you have the commands that I executed to give
     permissions just to the web server, so if you are interested in having other permissions you skip
     these command and execute another ones:
 1   mkdir -p web/media/cache
 2   HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep \
 3   -v root | head -1 | cut -d\ -f1`
 4   sudo chown $HTTPDUSER:$HTTPDUSER web/media/cache
     The bundle required configuration to work with Symfony is… “none”. This is a bit misleading
     because, in fact, the bundle is working right now and it’s not giving any error, but it’s not doing
     anything. Your work from now on will be editing the configuration file app/config/config.yml
     and adding the filters you want to your project. Now I’m going to explain you the *default** filters,
     but you must know that you are able to create your own filters.
     Thumbnail This filter creates a thumbnail from a specific image. The configuration may receive two
     different modes: the inset mode allows to make a relative resize, never exceedeing the limits given
     by the configuration; the outbount mode cuts the image if after making that relative resizing the
     dimensions are not correct. Yo may see it much better with an example configuration of the filter,
     as you know, placed in the app/config/config.yml file:
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb_in:
 4               filters:
 5                    thumbnail: { size: [32, 32], mode: inset } # Transforms 50x40 to\
 6    32x26, without cutting
 7           my_thumb_out:
 8               filters:
 9                    thumbnail: { size: [32, 32], mode: outbound } # Transforms 50x40\
10    to 32x32, cutting the width
     Both options allows an extra allow_upscale parameter that allows the image to be resized to a
     bigger size. By default, this parameters is set to false.
     Relative resize This filter allow to edit the size of an image with four different options. This
     configuration example explain its functionality:
     Chapter 10: LiipImagineBundle                                                                   86
 1   liip_imagine:
 2       filter_sets:
 3           my_heighten:
 4               filters:
 5                    relative_resize:       { heighten: 60 } # Transforms 50x40 to 75x60
 6           my_widen:
 7               filters:
 8                    relative_resize:       { widen: 32 }       # Transforms 50x40 to 32x26
 9           my_increase:
10               filters:
11                    relative_resize:       { increase: 10 } # Transforms 50x40 to 60x50
12           my_widen:
13               filters:
14                    relative_resize:       { scale: 2.5 }      # Transforms 50x40 to 125x100
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb:
 4               filters:
 5                    upscale: { min: [800, 600] }
Crop This filter allows tu cut the image form a detailed point, at a detailed size:
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb:
 4               filters:
 5                    crop: { start: [10, 20], size: [120, 90] }
Strip This filter allows to remove all image associated profiles and comments:
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb:
 4               filters:
 5                    strip: ~
     Background This filter allows to establish an image background color. By default, the color is #FFF
     (white):
     Chapter 10: LiipImagineBundle                                                                  87
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb:
 4               filters:
 5                    background: { color: '#00FFFF' }
It can also receive a detailed size, so it will create a new image with the size given:
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb:
 4               filters:
 5                    background: { size: [1026, 684], color: '#00FFFF' }
     Watermark This filter is cool because it allows you to add a watermark to your images keeping the
     aspect ratio:
 1   liip_image:
 2       filter_sets:
 3           my_image:
 4               filters:
 5                    watermark:
 6                        # Relative route to the watermark file (from "%kernel.root_d\
 7   ir%/")
 8                        image: Resources/data/watermark.png
 9                        # Watermark size relative to the image original size
10                        size: 0.5
11                        # Position: you can choose between topleft,top,topright,left\
12   ,center,right,bottomleft,bottom,bottomright
13                        position: center
     Auto rotate This filter rotates the image based on the EXIF information. If you are going to chain
     filters (I will explain this in a minute), you should call this filter before the others:
 1   liip_imagine:
 2       filter_sets:
 3           my_thumb:
 4               filters:
 5                    auto_rotate: ~
     Rotate This filter rotates the image based on an specific angle (degrees):
    Chapter 10: LiipImagineBundle                                                                           88
1   liip_imagine:
2       filter_sets:
3           my_thumb:
4               filters:
5                    rotate: { angle: 90 }
1   liip_imagine:
2       filter_sets:
3           my_thumb:
4               filters:
5                    interlace:
6                        # Los modos pueden ser none, line, plane, partition
7                        mode: line
    Create your own filter Besides all the information exposed in this section, in this link⁵³ you have all
    the needed information to create your own filter and make it work. Personally, I had never created a
    filter just because I haven’t needed it, but if you do need it, I will appreciate a lot if you share reason
    and the final result by Twitter so I can learn with you ;)
    Multifilter Do you need 2 or more filter for your images? No problem, you can chain them this way:
1   liip_imagine:
2       filter_sets:
3           carousel:
4               quality: 90
5               filters:
6                    relative_resize: { widen: 1920, allow_upscale: true }
7                    thumbnail: { size: [1920, 1040], mode: outbound }
      ⁵³http://symfony.com/doc/current/bundles/LiipImagineBundle/filters.html#custom-filters
      ⁵⁴http://symfony.com/doc/master/bundles/LiipImagineBundle/configuration.html
    Chapter 10: LiipImagineBundle                                                                    89
1   liip_imagine:
2       filter_sets:
3           aupa_thumbnail:
4                    filters:
5                        thumbnail: { size: [60,60], mode: outbound }
    The next step: download a big image and place it in the web/images/test.jpg folder (change the
    name for whatever you like). Once downloaded, let’s use that image in one of our Twig templates.
    For example, use the app/Resources/views/home/index.html.twig template. In order to use the
    filter, you just add the following code:
    Now, after browsing to http://127.0.0.1:8000/home you will be able to see the 60x60 image. It’s time
    to stop some minutes the reading and test the other filters. When you finish, continue with the next
    section: using the filters with PHP.
    What can you do with this route? Whatever you need, from sending it to a Twig to returning it as
    an API response. You also have the option to return the image as a reponse thank to the filterAction
    function:
    Chapter 10: LiipImagineBundle                                                                       90
    Tip 10
    Without any doubs this will be one of the tips that you are going to apply the most in your projects.
    As you should know, since the cell phone appearance, all websites have a big problem: the biggest
    resource files. This resources makes the mobile browsing not very successful because of the loading
    time. Besides it can make our visitants angry for consuming their data contract. In HTML5 you do
    have a possibility to reduce the image loading thanks to the <picture> tag.
    This tag receives a series of images that will be loaded depending on the visitant viewport resolution,
    so, if we add LiipImagineBundle with a bunch of filter to resize images automatically, we will have
    the perfect recipe up and running:
    The first step is declaring the needed filters:
1   liip_imagine:
2       filter_sets:
3           widen640:
4               filters:
5                    relative_resize: { widen: 640 }
6           widen1024:
7               filters:
8                    relative_resize: { widen: 1024 }
    Now, edit the app/Resources/views/home/index.html.twig template to use the <picture> tag with
    the recently created filters:
    Chapter 10: LiipImagineBundle                                                                      91
1   <picture>
2       <source media="(max-width: 640px)" srcset="{{ '/images/prueba.jpg' | imagine\
3   _filter('widen640') }}">
4       <source media="(max-width: 1024px)" srcset="{{ '/images/prueba.jpg' | imagin\
5   e_filter('widen1024') }}">
6       <source media="(min-width: 1024px)" srcset="{{ '/images/prueba.jpg' }}">
7       <img src="{{ asset('images/prueba.jpg') }}" alt="">
8   </picture>
    Do you understand what the previous code does? Inside the <picture> tag, all the <source> tags
    will be read until one of them meets the requirements. That one is the one that will be displayed
    on the screen. In case the browser does not support this tag, the <img> fallback will be rendered,
    awesome!
    However, not all browsers support this tag, but there exists a polyfill that will make it work in some
    of them. Here⁵⁵ you have the link to that polyfill.
    Summary
    In this chapter we have used a bundle for image treatment thanks to filters. With some small and
    simple configurations, the power that brings is humongous, and added to the tip 10 of this chapter,
    it make LiipImagineBundle as one of the must have bundles for every project.
    In the next chapter, we will see other bundles that you may be interested in, but we will not go into
    detail with them as we did in this chapter.
      ⁵⁵https://github.com/scottjehl/picturefill
     Chapter 11: ther bundles
     In this chapter I would love to show you some of the bundles that I’ve worked with in my professional
     career. These bundles have been a great help in certain moments. It’s not my intention to fill this
     chapter of documentation and use cases as I did in previous chapters, but I will give you little portions
     about what each of them do. If any of the explained bundles interest you, something that I’m really
     sure, you just have to try it and start working with it.
     EWZRecaptchaBundle
     Let’s start the chapter with a really simple bundle, but also very powerful. I’m sure that in more than
     one of your projects you must have used one of the infinite existing libraries for captcha elements.
     With this bundle, you will be able to create the famous Google ReCaptcha in a very ease way but,
     moreover, it’s fully integrated with the Symfony forms. In its GitHub repository⁵⁶ you can see how it
     is installed, how it is configured and one of the most important things: how it is used. Even though
     I said that I wouln’t copy any documentation, let me skip over this rule just once:
     Did you see how easy is to add a ReCaptcha field? You don’t need to map it to the entity and it will
     manage to launch an exception if the received value it’s not correct for it’s own. But, I must give you
     some “bad” news at this moment, “bad” because you may not find this casuistry in your projects.
     When rendering the ReCaptcha this way, at the time I’m writing this book, the bundle has no option
     to live together with other ReCaptcha fields in the same page. Do you dare with a pull request?
       ⁵⁶https://github.com/excelwebzone/EWZRecaptchaBundle
Chapter 11: ther bundles                                                                           93
AcceleratorCacheBundle
Things are changing a lot with the accelerator stuff in PHP, but even so, it’s interesting to know
how APC works as long as you have a PHP project with a PHP version below 5.5 (I hope you don’t).
Why this exact version? In PHP 5.5 and higher, we have OPCache by default, and before OPCache
we needed to install another byte code cache, as the mentioned APC.
Inside the app/config/config_prod.yml file you will be able to see different configurations ready
to be enabled depending on your project needs. All the elements that you enable will be sent to APC
cache and… what’s the problem with this? If you make any change in any of the elements that are
cached, you won’t see that changes until you clean the APC cache. That’s why this⁵⁷ bundle exists,
it will add a new Symfony command in order to clean the cached elements in an easy way, APC
and OPcache too.
FOSRestBundle
I’m going to give you some great news: you do have this bundle already installed because it’s
a SonataUserBundle dependency. And you will ask yourself, “what can I do with this bundle?
With FOSRestBundle⁵⁸ you will be able to create an API in a matter of minutes and with a lot
of added features out of the box. It’s a quite complex bundle because of the magic quantity it does
behind the scenes, so I suggest you to read the documentation carefully without rushing. Test every
documentation point and, very important, look others’ code to know how they use it. This is a well-
known and used bundle, and because of that it has it’s own documentation inside symfony.com.
NelmioApiDocBundle
I’ve heard that you recently created an API in your project, did you documented it? We, developers,
don’t like to document quite much but we have to make the effort and document everything we do
as good as possible. With this bundle⁵⁹ you will be able to document your API and the rest of the code
you develop easily, thanks to several annotations created for that goal. The bundle will be able to
create a website with all the stuff you developed and, of course, documented. In the bundle GitHub
documentation⁶⁰ you can see how to do what I told you here with some images which illustrate
how your documentation will be rendered as web mode. By the way: this bundle is already installed
thanks to SonataUserBundle too, so you don’t have any excuses to start working with it.
  ⁵⁷https://github.com/Smart-Core/AcceleratorCacheBundle
  ⁵⁸http://symfony.com/doc/master/bundles/FOSRestBundle/index.html
  ⁵⁹https://github.com/nelmio/NelmioApiDocBundle
  ⁶⁰https://github.com/nelmio/NelmioApiDocBundle/blob/master/Resources/doc/index.md
Chapter 11: ther bundles                                                                              94
DoctrineFixturesBundle
Fixtures allows us to do a controlled data load to the database. This inserted data can be used to run
tests or to do an initial load that the application we are developing requires. By default, Symfony
doesn’t have an easy way to load data as other frameworks or even CMS have, but this bundle will
help you to achieve it both with Doctrine ORM and ODM (MongoDB).
In the bundle documentation⁶¹ you can read about the installation (very interesting the App-
Kernel.php code where it gets instantiated) and the way to create load classes that implement
the FixtureInterface interface. Thanks to the load method that receives a manager (ORM or
ODM) as a paremeter and thanks to the app/console doctrine:fixtures:load and app/console
doctrine:mongodb:fixtures:load commands we will have a database full of information in
a matter of minutes. As you see in the previous documentaiton, this can get more and more
complicated, as much as you want. Here I would like to stop a bit and give you a little challenge:
our database doesn’t have a lot of entities so I suggest you to create an initial user loaded. The
“difficulty” here is that you will have to guess how the users are created thanks to Sonata User and
FOSUserBundle.
DoctrineMigrationsBundle
This bundle is an extension of the database abstraction layer and it offers you the possibility to
deploy new versions of your database programatically in a secure, easy and standarized way.
In the documentation⁶² you can read how it is installed and how it works, but all of this can be
sumarized in a bunch of migration files that contains the up() and down() methods. The up() method
contains the new stuff to be applied when launched (for example, “create the acme_hello table with
fields a, b, c, …“ ), while the down() method will only get called in case of undoing some deploy (for
example, “delete the acme_hello table”). The clases can be generated manually or just executing the
app/console doctrine:migrations:diff command which will generate the new class with all the
needs automagically. Of course, if you want to have the full control or just upload a concrete part
of those modifications, you can create and/or edit the generated file manually.
This bundle can be linked with the deploy system that will be explained in the chapter 15, and
without any doubts ot will make a before and an after in your deployments (or at least, it did to me).
FOSJSRoutingBundle
This bundle allows to generate routes in the “Symfony way” but from your JavaScript code. A lot
of people tell me that the solution is to insert that JavaScript “piece” of code in a Twig template and
there you will have access to the routing system, but, besides not beeing a best practice (just a patch),
  ⁶¹http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
  ⁶²http://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html
Chapter 11: ther bundles                                                                            95
this won’t solve the problem in all cases. If you need to pass JavaScript variables to the Symfony
route, you have no other option than create the route manually or use this bundle and work almost
as you have been doing until now.
You have everything you need to start testing it in the documentation⁶³. It may be a little weird
to start working with it if you are used to work with the Twig path and url functions and to the
$this->generateUrl of the Symfony controllers, but if it’s really necessary in your project, you will
get used to it very quickly.
HWIOAuthBundle
How many times did you visit a website and instead of filling up a boring and long form you
connected to it with your Facebook account? Everyday is more common that website allow you to
log in or even create a new account from a social network or from other usual website. In order to
make this work, the site from they get the information must implement the OAuth protocol. This
way, other developers can communicate with the site and obtain the needed information.
It may be possible that you had to create this whole process manually once in the past but, even
though it’s a very didactical way of learning how OAuth works, sometimes it’s much better not
losing time. HWIOAuthBundle⁶⁴ add the support to authenticate your users by OAuth 1.0a and
OAuth2 with more than 40 different providers, the main ones being Amazon, Dropbox, Facebook,
GitHub, Google, LinkedIn, Twitter, WordPress, Youtube and a very long etc. Unless you have a very
concrete need to develop this section from scratch, why would you do it? As always with Symfony:
spend your time in what you application really needs.
KnpSnappyBundle
Your website is awesome, but we get to the point that some users may need to download some
information as a report. Thanks to this bundle you will be able to create both pdf documents and
images from your HTML documents, and that’s because Snappy is wkhtmltopdf conversion utility
wrapper that it’s integrated in a trivial way into your Symfony project.
In the documentation⁶⁵ you will see that you must point out the wkhtmltopdf and wkhtmltoimage
binary locations, so before installing this bundle, go to this⁶⁶ website and download the needed files.
You wouldn’t have imagined that creating and returning pdf files was so easy, would you?
  ⁶³https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/blob/master/Resources/doc/index.md
  ⁶⁴http://knpbundles.com/hwi/HWIOAuthBundle
  ⁶⁵https://github.com/KnpLabs/KnpSnappyBundle
  ⁶⁶http://wkhtmltopdf.org/
Chapter 11: ther bundles                                                                             96
JMSTranslationBundle
I hope you already felt in love with Symfony at this point of the book and you already read the full
Symfony documentation, but for this bundle it will be desirable to have the translation⁶⁷ chapter
read. As you see, Symfony is based in some translation files that can be generated thanks to the
translations:update command but the whole process is very bad if we talk about usability.
Thank to this bundle you will be able to vitaminize this process incredibily. As you can read
in the documentation⁶⁸, first of all you have to extract the strings to be translated with the
translations:extract command and, thanks to some imported routes in our routing.yml file,
we will be provided with a simple but effective translation interface.
Stop translating the files manually or with external software such as Pootle that difficult the process.
Now we can even generate a specific permission like ROLE_TRANSLATOR and just give him/her
the access to our translator in where he/she will be able to see what it’s changing and where it’s
been changed, even though the changes won’t be shown until you clean the cache. If your website
is multi language, without any doubts this is a must have bundle.
Tip 11
In this tip I would like to remember you a previous commented thing and offer you two more
websites that I hope you use in your future projects. The first thing I want to remember you is that
you visit knpbundles.com⁶⁹ with asiduity and check the most used bundles and the ones that are
being updated. It’s important that you keep in mind the bundles maintenance by the people who
created them or the community, or it may be possible you find a problem in a very short period of
time.
The second thing I want to share in this tip is a Symfony cheatsheet. In http://www.symfony2cheatsheet.com/⁷⁰
in where you will find some of the elements that are used daily when developing. The website is not
fully updated but, such as the $this->redirectToRoute() function that I use a lot, but it’s always
a good idea to have this webpage to get a glimpse and the official documentation to get a detailed
view.
The last thing, if you are going to upload your project to a production environment, you may
be interested in reading this http://www.symfony2-checklist.com/ ⁷¹, where you will find elements
related to perfomance, security. etc.
As always, I like to give an extra, so I share with you the official SensioLabs YouTube channel⁷² in
where you will be able to see lots of professional people talking about Symfony related stuff, things
  ⁶⁷http://symfony.com/doc/current/book/translation.html
  ⁶⁸http://jmsyst.com/bundles/JMSTranslationBundle
  ⁶⁹http://knpbundles.com/
  ⁷⁰http://www.symfony2cheatsheet.com/
  ⁷¹http://www.symfony2-checklist.com/
  ⁷²https://www.youtube.com/user/SensioLabs
Chapter 11: ther bundles                                                                           97
that they have done and how they did it, or just how a Symfony component works in a more detailed
way. No doubts here: you will learn a lot from other professionals all over the world.
Summary
In this chapter we have seen a great variety of bundles thay may fit in some of your projects. Without
going into detail, we have explained the aim of all of them and it’s your turn to work with the ones
that looks interesting to you.
In the next chapter, we will go a little further with our Gulp system and we will be giving it even
more power.
    Chapter 12: Gulp (2)
    During this chapter we will improve our gulpfile.js and give it a lot more power than it already
    had. All the stuff it contains at this moment makes reference to the website styling, but it’s time
    to work with the JavaScript files and keep our code with a style guide. Having a good code is
    fundamental.
    Sass Lint
    I’m sure you edited other person fontend work and you thought “why does he/she works different
    than me?”. Everyone of us have a different way of working and it’s something bad that slows down
    future developers or frontenders. This is why style guides exist that are getting evolved periodically,
    and we should follow them. Just with this way we will be able to increase productivity almost
    immeadiately.
    A very clear example is the order for every element of our website styles. Some people write in the
    top part the spacing elements and the colors; others write the block disposition and then the sizes…
    which one is better? There is no better option so, the solution that was chosen was alphabetically
    ordered.
    Well, this just frightened you. You will be thinking in reading all your CSS styles again and see if
    everything is in the right place (although you already know that they are not). Again, Gulp to the
    rescue with a tool called Sass Lint. This tool will be able to analyze the SCSS files that you tell him
    to analyze and write the errors that are found on your screen.
    To install this tool, execute the following command in a terminal:
     The best option in this point for you is to configure the rules that your Sass lint should follow. In
     order to achieve so, create a .sass-lint.yml file in the root of your project and add there the rules
     you want. Here you have one of my configuration files:
 1   files:
 2     include: '**/*.scss'
 3   options:
 4     formatter: stylish
 5     merge-default-rules: false
 6   rules:
 7     bem-depth:
 8       - 0
 9       - max-depth: 1
10     border-zero:
11       - 1
12       - convention: zero
13     brace-style:
14       - 1
15       - allow-single-line: true
16     class-name-format:
17       - 1
18       - convention: hyphenatedbem
19     clean-import-paths:
20       - 1
21       - filename-extension: false
22         leading-underscore: false
23     empty-line-between-blocks:
24       - 1
25       - ignore-single-line-rulesets: true
26     extends-before-declarations: 1
     Chapter 12: Gulp (2)                                                                100
27     extends-before-mixins: 1
28     final-newline:
29       - 1
30       - include: true
31     force-attribute-nesting: 1
32     force-element-nesting: 0
33     force-pseudo-nesting: 0
34     function-name-format:
35       - 1
36       - allow-leading-underscore: true
37         convention: hyphenatedlowercase
38     hex-length:
39       - 1
40       - style: short
41     hex-notation:
42       - 1
43       - style: lowercase
44     id-name-format:
45       - 1
46       - convention: hyphenatedbem
47     indentation:
48       - 1
49       - size: 2
50     leading-zero:
51       - 1
52       - include: false
53     mixin-name-format:
54       - 1
55       - allow-leading-underscore: true
56         convention: >-
57           ^([\.\%]?[a-z]*[-]?[a-z0-9\-]*)(\.[a-z0-9\-]*)?(__[a-z0-9]*[-]?[a-z0-9\-\
58   ]*)?(--[a-z0-9]*[-]?[a-z0-9\-]*)?(\:[a-z]*)*$
59         convention-explanation: BEM pattern
60     mixins-before-declarations: 1
61     nesting-depth:
62       - 1
63       - max-depth: 3
64     no-color-keywords: 1
65     no-color-literals:
66       - 1
67       - allow-rgba: true
68     no-css-comments: 1
      Chapter 12: Gulp (2)                        101
 69     no-debug: 1
 70     no-duplicate-properties: 1
 71     no-empty-rulesets: 1
 72     no-extends: 0
 73     no-ids: 1
 74     no-important: 1
 75     no-invalid-hex: 1
 76     no-mergeable-selectors: 0
 77     no-misspelled-properties:
 78       - 1
 79       - extra-properties: []
 80     no-qualifying-elements:
 81       - 1
 82       - allow-element-with-attribute: false
 83         allow-element-with-class: false
 84         allow-element-with-id: false
 85     no-trailing-whitespace: true
 86     no-trailing-zero: 1
 87     no-url-protocols: 1
 88     no-vendor-prefixes:
 89       - 1
 90       - additional-identifiers: []
 91         excluded-identifiers: []
 92         ignore-non-standard: false
 93     placeholder-in-extend: 1
 94     placeholder-name-format:
 95       - 1
 96       - convention: hyphenatedbem
 97     property-sort-order:
 98       - 1
 99       - ignore-custom-properties: false
100     quotes:
101       - 1
102       - style: single
103     shorthand-values:
104       - 1
105       - allowed-shorthands:
106           - 1
107           - 2
108           - 3
109           - 4
110     single-line-per-selector: 1
      Chapter 12: Gulp (2)                                                                                 102
111     space-after-bang:
112       - 1
113       - include: false
114     space-after-colon:
115       - 1
116       - include: true
117     space-after-comma: 1
118     space-around-operator: true
119     space-before-bang:
120       - 1
121       - include: true
122     space-before-brace:
123       - 1
124       - include: true
125     space-before-colon: 1
126     space-between-parens:
127       - 1
128       - include: false
129     trailing-semicolon: 1
130     url-quotes: 1
131     variable-for-property:
132       - 0
133       - properties: []
134     variable-name-format:
135       - 1
136       - allow-leading-underscore: true
137         convention: hyphenatedlowercase
138     zero-unit: 1
      Finally, execute the same Gulp and see the result. If you haven’t edited anything of what we have
      done in this book, you should see something like this:
  1   app/Resources/assets/scss/components/_claim.scss
  2       warning Color literals such as '#f22' should only be used in variable decla\
  3   rations no-color-literals
          • A color that is not declared as a variable. Solution: declare it as a variable and use the variable
            always you need (_colors.scss)
      This time was easy, wasn’t it? Try to keep all your projects as clean as this example from now own.
      A little advice: modify the watch task so every time you edit a SCSS file you see if there are styling
      errors.
     Chapter 12: Gulp (2)                                                                                    103
     JSCS
     You already have your SCSS files in a unsurpassable way, but what about our JavaScript files? There
     are a lot of ways of checking the style guides, some of them are these ones:
         •   JSLint
         •   JSHint
         •   ESLint
         •   JSCS
     Personally I like quite a lot ESLint but, as always, I suggest you to read about all of them, see how
     they work and read about the pros and the cons of each of them.
     For our base project, we will be using JSCS. The first step is downloading the dependency:
The next step is configuring the Gulp task that will alert you with the found JavaScript file errors:
     JSCS needs a .jscsrc file in the root of your proyect that configures that it have to test in the process.
     If you don’t have that file in the root, you can add a configPath parameter and tell it where the
     file is. I take advantage of the file that comes by default inside the SonataCoreBundle vendor, but
     as always: research and create the one that best adapts to your project. Besides, in the gulp-jscs
     documentation⁷³ you have lots of possibilities to apply, a fix parameter that instead of alerting you
     with the errors, it will fix them for you. I know you like this, don’t you?
     In order to test that it’s correctly working, it’s time to create your first JavaScript file. For this demo,
     I created an app/Resources/assets/js/test.js file with the following content:
       ⁷³https://github.com/jscs-dev/gulp-jscs
     Chapter 12: Gulp (2)                                                                           104
 1   (function ($) {
 2     'use strict';
 3
 4     var string = "this is a string";
 5     var body = $('body');
 6   })(jQuery);
     If you want this file to be included in your Symfony project, remember to edit your app/Re-
     sources/views/base.html.twig template and add it to the corresponding section. For this example,
     this is not necessary.
     Now execute the gulp jscs task, what happens?
The example is very trivial, but it won’t be so trivial when your aplication starts growing.
     PHP CS
     Until now we have make our SCSS and JavaScript files more beautiful, but what about our PHP? It’s
     as important as the rest of them, even more knowing that we are working with a PHP framework,
     so letting this column alone it’s not an option.
     Chapter 12: Gulp (2)                                                                                105
     For this section, we are going to work with PHP Code Sniffer, with its gulp version. You can find it
     here⁷⁴. Firstly, the installation:
As always, create the gulp task that will execute the recently installed module:
     If you read this task, you will see that a binary bin/phpcs gets executed. You have to install it as
     well. To do so, you already have the necesary tool: Composer. Add the needed dependency to your
     composer.json file executing the folowing command:
     After this, you will have the necesary inside the bin folder automatically. It’s time to test if our PHP
     code is beautiful or if it’s not. Execute the gulp phpcs command again and… surprise! I’m sure you
     were not expecting so many errors at all. Don’t get frightened, it’s someting normal that some IDEs
     or code editors don’t comply.
     This binary, phpcs, can’t fix our code automatically so you will need to read the errors one by one
     and start fixing them… Well, I’m not going to be so bad and I’m going to help you a bit with this. If
     you see the binary files that the new Composer dependency has downloaded inside the bin/ folder,
     you will see the well known phpcs and another one: phpcbf. This is the one that will do magic and
     it will automatically fix the code… provided that it’s possible, indeed!
            NOTE: as an exercise, you should change the example standadrd ‘PSR2’ for the Symfony
            standar. You can read more about this here⁷⁵
       ⁷⁴https://github.com/JustBlackBird/gulp-phpcs
       ⁷⁵https://github.com/leaphub/phpcs-symfony2-standard
     Chapter 12: Gulp (2)                                                                             106
     PHP CBF
     It’s time to do some magic and have our errors fixed automatically. In the future, the next step will
     be that our website get automatically developed with just a few voice commands or something like
     that, but at this moment we will have to conform with the errors auto correction.
     Step number one: download the gulp requirements.
     As you guessed, you need one more element in this “recipe”. Step number two: create the gulp task
     using the new “ingredients”:
     Now, wave the magic wand executing the gulp phpcbf command and… et voilà, code clean and
     beautiful. Is there something that phpcbf was unable to fix? Well, it’s not a big problem opening a
     few files with the left errors and fix them manually after all the work it did for us, is it?
After installing it, create the needed tasks for your Symfony project::
     With this code you will be able to execute the needed Symfony commands to start working correctly.
     In addition to the previous shown tasks, I usually create a command that executes all of them and
     other one to start working:
    Chapter 12: Gulp (2)                                                                               108
    With these commands, once the Symfony is deployed locally with it’s dependencies, it’s enough
    executing gulp symfony and then gulp dev and start working.
    Now, it’s your decision if you want to apply this or not in your projects.
    Tip 12
    When you decide working with this type of technologies, you must have a good documentation
    explaining your users what the need to install. In our case, I have been telling you that you need
    to install Ruby (it was necessary before for the scss_lint gem, and it will be mandatory for the
    deployment with Capistrano), you need to install npm, and the rest of the elements that creates
    our base project. But, if you notice, everything is “packaged”. So: PHP dependencies are declared in
    a composer.json file. You install PHP and just with a composer install command, everything
    necesary gets downloaded. The same with Node: you install Node and npm and thanks to the
    package.json file and the npm install command you already have everything you need.
    This chapter tip has to do with this feature but in a Ruby environment. It would be great to tell
    developers to install Ruby and Rubygems and with just a single command everything needed gets
    installed. This feature is not done by default but thanks to a gem, this can be achieved. Besides these
    two ingredients, you hve to warn the rest of the developers to install the needed gem:
    This gem is able to read a Gemfile file placed in the same folder as the one from you will be executing
    bundle install. So, we must create a Gemfile in the root of our project and we copy the following
    content:
    You have to visit the https://rubygems.org/⁷⁶ website in order to see what you have to write in this
    file and be careful with possible breaks this may cause. However, if your project needs and exact
    version of a gem, thanks to this Gemfile all your teammates will use the same gem versions.
      ⁷⁶https://rubygems.org/
Chapter 12: Gulp (2)                                                                                109
Summary
In this chapter we have learnt to make Gulp more powerful and we have given it more sense in our
project. Thanks to the big opportunities it offers, we have improved our code quality and, the best
of all, practically effortless. There exist a lot more possibilites, and some of them will get mentioned
in the book epilogue.
     Chapter 13: additional Symfony
     configuration
     In this chapter we will be resting a bit from developing and we stop to see certain configurations
     that will allow you to either obtain an aditional element or improve your development time spent.
     Each of of this tricks, some of them seen in previous chapters, will help you sooner or later so be
     sure that you understand them as good as possible.
     ParamConverter
     In chapter 9 we saw one thing that, for me, is the great discover after a some time developing with
     Symfony: the ParamConverter annotation. It’s so powerful that some times it isn’t even necessary
     to write it, and that’s how I introduced it to you in that chapter’s tip. It sounds weird, but let’s see
     its behaviour extending the example we already saw and adding more examples.
     Let’s suppose that we have route that loads a blog post. The controller with the route as annotation
     should be somethinkg like this:
 1   /**
 2     * @Route("/blog/{slug}", name="blog_show")
 3     */
 4   public function showAction($slug)
 5   {
 6        $em = $this->getDoctrine()->getManager();
 7        $post = $em->getRepository("AppBundle:Post")->findBySlug($slug);
 8        if (!$post) {
 9            throw $this->createNotFoundException();
10        }
11        //...
12   }
     Each one of your routes will receive an id or a slug and it will have to repeat more or less this piece
     of code over and over… or maybe not. A conversion can be done directly to an entity this way:
    Chapter 13: additional Symfony configuration                                                       111
1   use AppBundle\Entity\Post;
2
3   /**
4     * @Route("/blog/{slug}", name="blog_show")
5     */
6   public function showAction(Post $post)
7   {
8        //...
9   }
    As you see, now I forced to receive a Post as a parameter. How does this class of magic work? In the
    route there exist a placeholder called “slug”. In our function we are reciving a Post type entity. What
    the code is going to do internally is checking that the Post class contains a property called slug and
    it will find by that field. In case it is not found, it will automatically launch a 404 exception.
    The problem comes when parameters are not named as in the entities, when we have to make a
    previous modification (lika a date) or when we have parameters that belong to different entities. In
    those cases, we need the “@ParamConverter” annotation and, in some of them, a little code that will
    be written in some of our repository files.
    We complicate the example a little more, and now we want both the blog post and a post comment:
1   /**
2     * @Route("/blog/{id}/comments/{comment_id}")
3     * @ParamConverter("comment", class="AppBundle:Comment", options={"id" = "commen\
4   t_id"})
5     */
6   public function showAction(Post $post, Comment $comment)
7   {
8        //...
9   }
    Obviously, we cannot use the {id} placeholder twice in the same route, so one of them has to be
    called in a different way. For that reason, we have to map the comment_id parameter to the Comment
    id of the entity. Even so, this is very simple, but check this example:
     Chapter 13: additional Symfony configuration                                                        112
 1   /**
 2     * @Route("/user/{first_name}/{last_name}")
 3     * @ParamConverter("user", class="ApplicationSonataUserBundle:User", options={
 4     *    "repository_method" = "findByFullName",
 5     *    "mapping": {"first_name": "firstName", "last_name": "lastName"},
 6     *    "map_method_signature" = true
 7     * })
 8     */
 9   public function showAction(User $user)
10   {
11        //...
12   }
     Wow! What was that? Ok, let’s go step by step. The first thing we can read is the route, which
     receives both the name and the surname of, supposedly, a user from our database. We are indicating
     to the ParamConverter to execute the function findByFullName located in the UserRepository.php
     repository file that will receive the parameters also received in this route. This function creation is
     on our own, so let’s do it:
 1   class UserRepository
 2   {
 3       public function findByFullName($firstName, $lastName)
 4       {
 5           //...
 6       }
 7   }
     This function will return a User and, in addition to be used in our route and save a piece of code,
     it’s a function perfectly reusable by the whole platform, something that without any doubts follows
     the best practices that we have been talking about several times during the book.
     These and many other examples, besides the creation of your own converters can be found in the
     official Symfony documentation⁷⁷ of the @ParamConverter annotation.
    What sense does this make, going through a controller just for rendering a template? This is
    something that cannot be done with annotations but, as we saw in the chapter 8 tip and I repeat in
    this section, we have a routing YAML file called routing.yml that will help us to avoid this litte and
    “useless” code. It’s as easy as adding a route like this in your routing file:
1   app_privacy:
2       path: /privacy
3       defaults:
4           _controller: FrameworkBundle:Template:template
5           template:    static/privacy.html.twig
    If you read the HTTP Cache part of the Symfony book, you are going to tell me that with this system
    the page cannot be cached. Don’t worry, here you have the solution for this problem too:
1   app_privacy:
2       path: /privacy
3       defaults:
4           _controller:         FrameworkBundle:Template:template
5           template:            'static/privacy.html.twig'
6           maxAge:              86400
7           sharedAge:           86400
    And, as in the previous example, the solution is in the routing.yml file this way:
    Chapter 13: additional Symfony configuration                                                       114
1   redirect_route:
2       path: /old-page
3       defaults:
4           _controller: FrameworkBundle:Redirect:urlRedirect
5           route: new_route
6           permanent: true
    This “trick” was also seen in chapter 8, but I didn’t tell you that it admits several configurations. I
    explained you the tipical example so you know it’s existence, and your homework for this section
    is reading this documentation⁷⁸ to know the possibilities that there exist.
    Dump autoload
    You should already read about this in the checklist I shared with you previously, but it’s so simple
    and important that I expend a little section in this chapter.
    When you execute composer to get the needed dependencies, an autoload which is not optimized
    gets generated. They say that it can even slow down quit much if you have lots of classes in your
    project. For that reason, an specific command was created so the generated autoload becomed an
    improved version, and because it’s “free”, I suggest you to use it in your production environment:
    Hide logs
    If you ever check what is inside the log files generated in the var/logs folder, you will see that
    there exist a lot of information that isn’t necessary, or at least in the development log file. This
    mainly lines that are not contributing at all: the events. Symfony uses monolog library as the log
    file manager with a default configuration that I like to change everytime I create a new Symfony
    project. The aim here is to make those lines disappear so our logs are full of relevant information.
    Edit the app/config/config_dev.yml file to remove the events:
1   monolog:
2       handlers:
3            type: stream
4            path: %kernel.logs_dir%/%kernel.environment%.log
5            level: debug
6            channels: "!event"
 1   monolog:
 2       handlers:
 3            mail:
 4                type: fingers_crossed
 5                action_level: critical
 6                handler: buffered
 7            buffered:
 8                type: buffer
 9                handler: swift
10            swift:
11                type: swift_mailer
12                from_email: error@website.com
13                to_email: webmaster@website.com
14                subject: Critical error detectd
15                level: debug
     PhpStorm configuration
     When you are developing with the app_dev.php front controller (by default with bin/console
     server:run), the error screens are quite descritive, but you can go a further step. If you development
    Chapter 13: additional Symfony configuration                                                        116
    IDE is PhpStorm, you can enable a little configuration that will allow you to click on the error that
    is displayed on those screens so that concrete file will be opened at the exact line of the error, ready
    to fix!
    In order to do this, you shuld edit the app/config/config.yml file and add the following configu-
    ration:
1   framework:
2       ide: "phpstorm://open?file=%%f&line=%%l"
If after this you weren’t using PhpStorm, this is a good moment to, at least, give it an opportunity.
1   framework:
2       session:
3           handler_id: session.handler.native_file
4           save_path: "%kernel.root_dir%/sessions/%kernel.environment%"
    Tip 13
    You may not have an SMTP server for sending emails or you may don’t want to you your Gmail
    account for this, so what’s the solution? If you try to search for it in Google you will see that there
    exist several free possibilities, but the one that is working the best is SparkPost. Nowadays, with the
    free account you will be able to send up to 100,000 emails/month. If you want special features or
    more email quantity, you will have to pay for the service. But I think 100,000 emails is quite a huge
    amount of emails to make some tests… isn’t it?
    To configure SparkPost in Symfony, you will have to make some little changes to the Swiftmailer
    configuration. First of all: edit the app/config/config.yml file with the following code:
    Chapter 13: additional Symfony configuration                                                     117
1   swiftmailer:
2       transport:          "%mailer_transport%"
3       host:               "%mailer_host%"
4       username:           "%mailer_user%"
5       password:           "%mailer_password%"
6       spool:              { type: memory }
7       port:               "%mailer_port%"
8       encryption:         "%mailer_encryption%"
    If you compare this with the default configuration, you will see that I added the port and encryption
    parameters. Both will get their values from the app/config/parameters.yml file so, depending
    where the project is located, the behaviour will be changed. You must help other users to deploy
    the project, and for that, you must edit the app/config/parameters.yml.dist file so the terminal
    will ask them to fill the new values when the run a composer install. Edit the file and add the
    default parameter values:
1   mailer_transport:            smtp
2   mailer_host:                 127.0.0.1
3   mailer_user:                 ~
4   mailer_password:             ~
5   mailer_port:                 25
6   mailer_encryption:           null
    In your case, as you already have the app running in your computer, you should fill the parame-
    ters.yml file with the needed information. To get this information, you have to register yourself
    at https://www.sparkpost.com/⁷⁹, and after following the first steps, you will be prompted with the
    needed configuration. Be careful! The password won’t appear again so, if you lose it, you will have
    to generate a new one. You will also need to validate the domain from where the emails will be sent.
    Read the SparkPost documentation if you don’t know what I’m talking about. With the parameters
    given, edit your app/config/parameters.yml file:
1   mailer_transport: smtp
2   mailer_host: smtp.sparkpostmail.com
3   mailer_user: SMTP_Injection
4   mailer_password: PASSWORD
5   mailer_port: 587
6   mailer_encryption: tls
    You just need to edit the mailer_password parameter, which is the password that the website gave
    to you. With this done, you will have your Symfony sending emails from SparkPost.
      ⁷⁹https://www.sparkpost.com/
Chapter 13: additional Symfony configuration                                                       118
Summary
In this chapter we have seen some Symfony interesting configurations. There exist lots of configu-
rations but these ones are the configurations that I mostly use in my projects. In this link⁸⁰ you will
be able to see the configuration section of the Symfony Cookbook, a book in where you will find the
best additional recipes for your Symfony. You should check it quite often, they add some interesting
things constantly.
  ⁸⁰https://symfony.com/doc/current/cookbook/configuration/index.html
Chapter 14: modifying
SonataUserBundle
Until this point, we left apart the user stuff because it was given to us “for free”. But it’s possible
that the default features don’t fit to what your project needs. In this chapter we will see some of the
interesting configurations that you can edit in this bundle and some changes that you can accomplish
with code.
Modifying FOSUserBundle
Even if you have installed SonataUserBundle, as you should already know, this bundle directly
depends on FOSUserBundle, so we will check in detail this one because it’s the user base. Everything
you are about to do is in this section⁸¹ of the documentation, but you will be applying what you read
into practical examples. Be careful because, even the documentation is for FOSUserBundle, what we
are editing is the bundle that makes use of it: SonataUserBundle. Let’s start!
    In both options, the templates path must be exactly the same, both folder names and file names.
    Besides, you have to keep in mind that Symfony will look for the template firstly in the app/ folder,
    then in a child bundle and lastly in the concrete bundle, but once it’s found in that order, it won’t
    keep on searching.
    What we are going to do in this example is editing the FOSUserBundle base template. To do so,
    you must have a little knowledge about how the views are structured in this bundle. Browse to
    vendor/friendsofsymfony/user-bundle/Resources/views and take a break to see what’s inside
    that folder.
    Are you ready? As you have seen, the layout.html.twig template is in the root of the views, just
    the one we want to edit. As all your templates reside in the app/ folder, the correct thing is that you
    keep on creating all the templates there, even the overriding ones. Here comes the trick: you have to
    create the exact same folder structure that you are about to edit, so create the app/Resources/FOS-
    UserBundle/views/ folder and in the root of it, copy the previous layout.html.twig that we saw in
    the Sonata vendor. Once you did this, you must clean the Symfony cache so it “finds out” the new
    template:
1 bin/console cache:clear
    From now on, you do have the control of the base template. Before making any modifications
    without any aim, look what the default template contains and try to imagine how you can fit it
    into you base template. Try to do it yourself, but I leave here my own solution, because there are
    plenty of them:
1   {% extends 'base.html.twig' %}
2
3   {% block content %}
4       <div>
5           {% block fos_user_content %}
6           {% endblock fos_user_content %}
7       </div>
8   {% endblock content %}
    If you now open the login page http://127.0.0.1:8000/login you will se your base template working
    but… there’s a “little” error. The right part of the page, the sign in section, it’s a full embedded page,
    so the headline and the footer are there too. Why is this happening?
    It’s important that you understand what’s happening. Open the login template file and search for
    the part that shows the embedded sign in section. It’s exactly this one:
     Chapter 14: modifying SonataUserBundle                                                                 121
 1   {% extends "@FOSUser/layout.html.twig" %}
 2
 3   {% block fos_user_content %}
 4   {% include "FOSUserBundle:Registration:register_content.html.twig" %}
 5   {% endblock fos_user_content %}
     Until the last version, for Symfony 2.*, that extends in the first line of the the file was creating two
     layouts in the same page. You couln’t override this template and delete the extends line because,
     this way, if you browsed to http://127.0.0.1:8000/register/, you found a register form without any
     layout. The solution was easy: edit the block with renders the sign in form in the login.html.twig
     template with an HTML code that explains the benefits of signing in providing a link to the sign in
     page. In fact, this is what lots of websites usually do, but as we like the “difficult stuff”, we are going
     to override the controller.
     In the previous section we were interested in modifying the registerAction inside the Registra-
     tionController provided by FOSUserBundle. There are no controllers within the app folder so this
     time, you have to create the neede code inside your UserBundle, in the src folder.
     In the Controller folder of this bundle, create the same file as it is in the vendor, RegistrationCon-
     troller.php, with the function you want to override:
 1   <?php
 2
 3   namespace Application\Sonata\UserBundle\Controller;
 4
 5   use FOS\UserBundle\Controller\RegistrationController as BaseController;
 6
 7   class RegistrationController extends BaseController
 8   {
 9       public function registerAction()
10       {
11           //...
12       }
13   }
     Do you see the trick here? We wrote a use of the original controller and we renamed it as
     BaseController because of the name collision. After this, we make the controller extend the original
     one and that’s all. It’s time to insert the logic we want. We don’t want to change a lot really so just
     copy and paste the original code and edit the response using the new template you want to render.
     With this method, you have to be careful when you upgrade your versions because de default
     controller can changed and you must adapt yours. Check it periodically and make the needed
     changes to your controller so you keep it updated.
 1   security:
 2       providers:
 3           fos_userbundle:
 4               id: fos_user.user_provider.username_email
     This just makes the first step because, with this configuration, the register will still ask the user to
     fill in the username and the email, and you know that both things should be the same. Here you
     have some homework: you should change the sign in form so it just ask for the email. In addition
     to this, when the email is established, this should be copied to the username or the database will
     launch an error when creating the user.
     You can do this by you own, come on! If you want a little help for the last part, here you have the
     modification needed to you User.php file:
 1   /**
 2     * Overrides default setEmail function
 3     * @param String $email
 4     */
 5   public function setEmail($email)
 6   {
 7        $email = is_null($email) ? '' : $email;
 8        parent::setEmail($email);
 9        $this->setUsername($email);
10   }
Moreover, remember to override the RegistrationFormType.php. In this link⁸² there is a little help.
 1   fos_user:
 2       # ...
 3       registration:
 4           confirmation:
 5               enabled: true
       ⁸²https://symfony.com/doc/master/bundles/FOSUserBundle/overriding_forms.html
    Chapter 14: modifying SonataUserBundle                                                                124
    You have finished. With this configuration done in the app/config/config.yml file your users will
    receive an email notification when they sign in and they won’t be able to log in until they confirm
    their email account. Be sure to configure your SMTP server or the won’t get any emails!
    On the other hand, there exist a lot of emails that are sent from FOSUserBundle and you can edit
    as any other template. What you cannot edit in those templates is who sends them, and some other
    stuff. This configuration and some other are described in this link⁸³. I don’t consider important to
    copy and paste the full configuration written on this page so, once you read it, continue with the
    following section.
    Other modifications
    In the previous sections I wanted to share with you the modifications that I usually manage in my
    projects. I also shared the link to the part of the documentation in where it is explained all these
    modifications and many others than can be done, and I consider that reading them is very interesting.
    It’s so interesting that I share again the same link so you read all the information slowly. You don’t
    have to do this right know, but keep in mind where it is when the moment arrives.
           Documentation: https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#next-
           steps⁸⁴
    Tip 14
    The User base class that we created earlier on just contains an $id and everything else is
    inherited from SonataUserBundle. Obviously, it’s possible that you need more fields for your
    projects. Those fields will be added in the entity you created in previous chapters, located in
    src/Application/Sonata/UserBundle/Entity/User.php. But you have one more problem with
    this: everything you add won’t be rendered in the corresponding admin.
    If you want to show the new fields in the admin, you have to edit it. The first step is telling the Sonata
    bundle to take your admin class instead of their default one. Edit the app/config/config.yml file
    with this modification:
1   sonata_user:
2       admin:
3           user:
4                class: Application\Sonata\UserBundle\Admin\UserAdmin
    After this, you have to create the class seen above, so create your UserAdmin.php file with the
    following content:
      ⁸³https://symfony.com/doc/master/bundles/FOSUserBundle/emails.html
      ⁸⁴https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#next-steps
     Chapter 14: modifying SonataUserBundle                                                            125
 1   <?php
 2
 3   namespace Application\Sonata\UserBundle\Admin;
 4
 5   use Sonata\AdminBundle\Datagrid\ListMapper;
 6   use Sonata\AdminBundle\Form\FormMapper;
 7   use Sonata\UserBundle\Admin\Entity\UserAdmin as BaseUserAdmin;
 8
 9   class UserAdmin extends BaseUserAdmin
10   {
11       protected function configureFormFields(FormMapper $formMapper)
12       {
13           parent::configureFormFields($formMapper);
14       }
15
16        protected function configureListFields(ListMapper $listMapper)
17        {
18            parent::configureListFields($listMapper);
19        }
20   }
     As I gave you the previous code, everything will be like the default admin. It’s time to edit the list
     fields, the form fields or whatever other section you want to edit with your needs. You can even
     delete the parent:: code line and rewrite everything completely, you have the power!
     Summary
     In this chapter we have learnt to manage a little better our platform users editing or adding some
     features that give our project more power and flexibility. With this, we finished developing our solid
     base for our project, the next step will be moving it to a production stage. In the next chapter you
     will learn how to do a deployment with a tool created for it.
Chapter 15: deployment
I’m sure you suffered this chapter more than once. The first deployments were done by FTP,
uploading all files or, if you certainly new the ones that have been edited, just those ones. After
this situation, lots of developers started using a version control software such as Git to do the
deployments much easier, because you just have to bring the needed changes in a much controlled
and secure situation, being able to go back in case of error or something special.
Nowadays we use deployment systems. There are a great variety of them and written in different
development languages, so in this chapter, we are going to see some of the different possibilities that
exist without going into detail and we will create a full deployment with one of them.
Magallanes
Magallanes is a deployment tool written in PHP that allows you to deploy mainly other PHP tools.
It’s very easy to use and manage, but it’s specially easy to extend adding our own tasks. It allows you
to deploy your application to any servers you want, both by rsync and by ssh, and after this, execute
the needed tasks so the deployment ends the way we want, such as the Composer dependency
installation.
It’s very easy to use this tool with Symfony because it’s installed through Composer and the creator
is a fan of this framework, so there are lots of tasks by default that works quite good with our
deployment. However, after testing it in some professional projects, the tool was lack of features,
such as shared folders and files management that don’t change over deployments: parameters.yml,
var/logs, …
Anyway, here⁸⁵ you can read a post written by me about how to deploy your Symfony application
with magallanes. It’s not fully functional but that missing stuff is easily fixable with the tasks we
are able to create.
Ansible
This tool is huge and allows a great variety of actions. Just seeing the companies using the tool in their
homepage gives you an idea of the power this software has. It’s written in Python, a development
language that is trendy right now, but this shouldn’t make you leaving this tool aside.
I’ve heard a lot of people saying that this is the “definitive tool”, but I’m a great defender of using
the tool that best fits your needs, including the need to be comforable with it. Personally, it was a
  ⁸⁵https://medium.com/@jontorrado/deploying-a-symfony2-project-with-magallanes-28abe452c54c
Chapter 15: deployment                                                                               127
bit difficult to get in touch with this tool, even I can see it’s power. On the other hand, if you search
on GitHub you will see a lot of people using Ansible as a deployment tool for their Symfony project.
Just search for “Symfony ansible” and you will see the results.
We are not going to use this tool in this book but here you have a post written by me about how to use
Ansible to deploy Symfony applications: https://medium.com/@jontorrado/deploying-a-symfony-
application-with-ansible-c517c26ccbfc⁸⁶.
Capifony
It’s possibly the most used tool to make Symfony deployments… or at least it was. This is
because of the use of Capistrano 2 behind the scenes, and there’s been a while since Capistrano
3 was launched with lots of improvements. Even so, I suggest to take a glimpse at their webpage
http://capifony.org/⁸⁷.
The tool is written in Ruby but the only thing you should know about Ruby is “copying and pasting”,
because the great majority of the configurations are already done for you and you just have to change
the server IPs, the folders and some parameters like that. If you browse to their GitHub repository
you will see that more than 100 people contributed to this software with more than 700 commits,
something to keep in mind if what you want is an active community that keeps the software updated
(Capifony is unmaintained bacause of Capistrano 3).
After reading this, you may feel that you want to try Capistrano 3 so, let’s start with the action and
read the next section of the chapter.
Capistrano
Capistrano is a remote server automation tool written in Ruby. It supports the development and
execution of tasks and it includes a great variety of them by default for deployment tasks. As you
imagined, it’s also valid for many other things apart from deployments sot, won’t this tool be quite
big for what we want?
I’ve always heard the sentence “go to buy the bread with a Ferrari”, and lots of times we act like
this. We complicate our developments with huge and difficult software to create something tiny
that won’t use almost anything that we created as a base. However, there are some exceptions that
worths it because they don’t penalize our development time. In addition, if we want to increase the
features in a future and automate something remote, we won’t be able to do this with Magallanes
and we would have to change everything to a system like Ansible or Capistrano, with the time we
will be losing in this change.
So my advice here is that you use a one of these systems that already have a default configuration
created by the community and will be extremely easy to start using it just copying and pasting some
  ⁸⁶https://medium.com/@jontorrado/deploying-a-symfony-application-with-ansible-c517c26ccbfc
  ⁸⁷http://capifony.org/
    Chapter 15: deployment                                                                            128
    piece of codes. In most of my projects I use Capistrano as a deployment system, so in the next section
    I will explain you how to deploy a Symfony application with a plugin for Capistrano 3 created for
    this.
1   source 'https://rubygems.org'
2   #...
3   gem 'capistrano-symfony', '~> 1.0.0.rc1'
    After executing the installation command bundle install, it’s possible that you see a WARNING
    in the installation. Don’t worry about it, it’s just because the gem changed it’s repository but you
    have the good one, they just left the message there.
    The next thing is configuring our Capistrano in order to load all the requirements for the deployment.
    Create a Capfile file in the root of your project with the following content:
1   # Capfile
2   require 'capistrano/setup'
3   require 'capistrano/deploy'
4   require 'capistrano/symfony'
5   require 'capistrano/scm/git'
6   install_plugin Capistrano::SCM::Git
    That setup require is needed for the Capistrano::DSL:Module stages; the deploy require is for the
    tasks that we are going to define in the last part of our deploy:***; the symfony require contains
    all the needed dependencies, such as capistrano/composer that we will obviously need for the
    dependecy installation.
    With this done, create a config folder in the root of your project and, inside it, create a config/de-
    ploy.rb file. Here you must configure all the tasks that have to be done in each server, wherever is
    going to be deployed. Before doing anything I must tell you that your project should be inside a Git
    repository, so I will suppose you already did that. If you don’t, you can use Bitbucket or GitHub to
    continue with the example:
     Chapter 15: deployment                                                  129
 1   ############################################
 2   # Setup project
 3   ############################################
 4
 5   set :application, "aupa-bilbao"
 6   set :repo_url, "https://github.com/groupname/repository.git"
 7
 8   ############################################
 9   # Setup Capistrano
10   ############################################
11
12   set :log_level, :info
13   set :use_sudo, false
14
15   set :ssh_options, {
16     forward_agent: true
17   }
18
19   set :keep_releases, 3
20
21   ############################################
22   # Linked files and directories (symlinks)
23   ############################################
24
25   set :linked_files, ["app/config/parameters.yml"]
26   set :linked_dirs, [fetch(:log_path), fetch(:web_path) + "/uploads"]
27   set :file_permissions_paths, [fetch(:log_path), fetch(:cache_path)]
28
29   set :composer_install_flags, '--no-interaction --optimize-autoloader'
30
31   namespace :compile_and_upload do
32
33     desc 'Compile and upload'
34
35     task :gulp do
36       if fetch(:env) == "prod"
37         run_locally do
38           execute "gulp prod"
39         end
40       else
41         on roles(:all) do |host|
42           execute "cd #{release_path}; npm install && gulp prod"
     Chapter 15: deployment                                                                           130
43         end
44       end
45     end
46
47     task :upload do
48       if fetch(:env) == "prod"
49         on roles(:all) do |host|
50           upload! "#{fetch(:web_path)}/css", "#{release_path}/#{fetch(:web_name)}/\
51   css", recursive: true
52           upload! "#{fetch(:web_path)}/js", "#{release_path}/#{fetch(:web_name)}/j\
53   s", recursive: true
54         end
55       end
56     end
57   end
58
59   namespace :deploy           do
60     after :updated,           'composer:install_executable'
61     after :updated,           'compile_and_upload:gulp'
62     after :updated,           'compile_and_upload:upload'
63   end
     What the hell is this? Read it again, slowly, because it’s Ruby and you may haven’t notice that you
     really understand almost everything written in the previous piece of code. You just have to know
     two things:
     The first part of the code is just configuring some deployment variables. If you don’t understand
     some of them, you can read what they do in the Capistrano documentaton or in the capistrano-
     symfony documentation, depending on the variable name. In the second part, what I do is create
     two different tasks: the first one for Gulp and the other one for the needed file upload, why?
     We are going to make a difference between the testing servers and the production servers. It’s
     possible that you may have npm installed in the machine with all the rest of the needed software,
     just they way we work while developing. However, in a production environment, we don’t need to
     have all that software installed, just with the final CSS and JS files is enough, and the rest of the
     software is definitely not needed.
       ⁸⁸http://capistranorb.com/documentation/getting-started/flow/
       ⁸⁹https://github.com/capistrano/symfony/#settings
     Chapter 15: deployment                                                                             131
     Right now you are wondering where is the testing and the production configuration. For that, we
     must create a deploy folder inside the recently created config folder and there, create a file for each
     deployment stage we want. For this example, create the config/deploy/pre.rb that references the
     testing environment:
 1   ######################################################################
 2   # Setup Server
 3   ######################################################################
 4   server "dev.company.com", user: "sshuser", roles: %w{web}
 5   set :deploy_to, "/path/to/your/deployment/directory"
 6   set :env, "dev"
 7
 8   ######################################################################
 9   # Capistrano Symfony - https://github.com/capistrano/symfony/#settings
10   ######################################################################
11   set :file_permissions_users, ['www-data']
12   set :webserver_user, "www-data"
13
14   ######################################################################
15   # Setup Git
16   ######################################################################
17   set :branch, "master"
     This file is much simpler and you just have to declare the variables that modify the deploy.rb main
     file. The first line is the server to be connected with an SSH conection to deploy the project. Here
     you can write down your own computer IP if you have a SSH server running.
     In the deploy_to variable you must write the path in where the Symfony project will be deployed.
     In the next section we will talk about the final folder result in the destination.
     The env variable references the stage environment and, if you remember our deploy.rb file, this
     variable will make our assets to be compiled locally and then uploaded or do this process in the
     destination server. If you establish this variable as “dev”, the system will suppose that you have
     Gulp installed in the destination computer, so make sure that it’s really installed.
     The file permission variable and the webserver variable are used for the cache, logs and web/uploads
     folder. They allow that user to have write permissions on that folders. You should know, of course,
     what’s your server webserver username, of you will find an important error that won’t allow you
     to view the deployed website.
     Lastly, you must tell Git the branch to “pull” the infomation from. Logically, in the testing stage and
     in production stage, it’s very possible that you have different branches, so this is where you must
     edit that configuration.
     You may have got mad with this section, so read it calmly, test this in your computer and you will
     se that it’s very easy. Besides, I want to add another thing: in the rest of your projects you can copy
    Chapter 15: deployment                                                                                                132
    and paste every single line that we’ve done here and, just changing some basic lines, you will have
    your deployment system ready to go.
    But now, how do you start the process? You must remember the name of the files you created inside
    the config/deploy folder. Those are the ones who tell you the stage for the following command:
    There it is! Everything worked like a charm (I hope so), but you just realized that your code contains
    a very important bug, can you go back? If you previously did a deploy, you can go back to a
    previous deployment with the information provided in this link⁹⁰. Moreover, you may need to edit
    the deploy.rb file so the “undo” tasks get done, such as database modifications.
    Do you remember the DoctrineMigrationsBundle”? It’s that to start using it and you take advantage
    of the Capistrano deployment flow to launch the different commands that the bundle offers you.
    Here⁹¹ you have all the needed information so you can test it.
    To finish this section, some homework for you: we didn’t keep in mind the sessions folder until
    now, so everytime a deployment is done, we will have a problem. Modify the deploy.rb file so the
    app/sessions folder is also kept between deployments.
        • A repo folder with all the version control software related stuff
        • A releases folder with a folder for each deployment
        • A current symbolic link that links to a folder inside releases, normally the latest succesful
          deploy
        • A shared folder that contains all the files and folders that won’t change between deployments,
          such as configuration or uploads (and the homework!)
    I hope this helps you in a big way to reduce your deployment time and the problems you could have
    when doing it by hand or when doing it with a version control software.
    Tip 15
    The tip with this chapter is system engineering related, something that lots of developers know
    nothing about but I love. When you are about to deploy, both with the Capistrano showed tool or
    with sFTP or SSH, you will need a server password to connect. This is a security problem, but it’s
    also very easy to solve.
    We must ask the user who is going to deploy a computer identification and then give that computer
    the permission to connect with a concrete user. That computer will have the permission to connect
    to that computer with that user without password, something that reduces the security problem
    because we won’t give password to anybody and we will have the people who are able to connect
    controlled. In order to achieve this, you must follow these steps.
    The user who is going to connect must give the system administrator his/her public key. He/she may
    have one created. To check it, just execute the following command:
1 ls -al ~/.ssh
    Inside that folder there should exist an id_rsa.pub file whose content is what we need. If that file
    is not created or if you want to create another now, you must execute the following command:
    With this command, you should have that file created. The content of this file will be given to the
    system administrator.
    That system administrator will have to connect to the destination server. In it, he/she must browse to
    the authorized_keys file located in the .ssh folder too, which is also located in each user home. So,
    the $HOME/.ssh/authorized_keys file must be edited, adding the content of the given id_rsa.pub.
    When this is done, the user will be able to do a ssh user@server.domain.com and log in without
    password, so he/show won’t be asked for a password when doing a cap pre deploy, fantastic!
          Note: some server distributions have the authorized_keys conection disabled by default.
          Edit the SSH configuration to check if the feature is enabled. Also, check the file
          permissions: 600 to .ssh and 700 to authorized_keys.
Chapter 15: deployment                                                                        134
Summary
In this chapter we have learnt to deploy our Symfony project to a remote server with different
options. The example done here contains a solid base for your future projects but it’s also easily
extensible for your future requirements. With this, we have completed the wholy cycle of our
application development, so the last thing to talk about is how tests are done, explained in the
next chapter and written by Beñat Espiña.
Chapter 16: testing
As we are developers that love the things well done, you must keep in mind this chapter quite a lot
because it’s about a topic that nowadays is taking more and more relevance, but not as much as it
should take: testing.
The purpose here is to do an interesting introduction about what the testing world contains, specially
in PHP, and obviously for our loved Symfony framework.
PHP is a development language with certain age and, like any other old stuff related to development,
it comes from a dark period, totally ignoring every design pattern and best practices about software
architecture. But this is the past because for some years, people like the Symfony creator, Fabien
Potencier, with many others, have forced PHP to become a serious language, taking advantage of
other language own features like JAVA or Python. Version 5 was just the first step for what is was
going to come and in few years, we move away from the 5000 lines WordPress functions.php mixing
superglobal variables with functions and HTML everywhere, to a complete complex and robust
ecosystem, with tools like this book’s framework, Composer as a dependency manager, Doctrine as
ORM, classes, namespaces, QA tools everywhere and lots of other things. This brought another way
to develop with PHP using the previous mentioned software architectures, and from this the DDD
and microservices concepts, and the appliance of design patterns like inheritance, polymorphism or
dependecy injection. The PHP growth as language is now unstoppable so take a seat because PHP7
is here with lots of interesting updates.
services nor databases. However, funtional tests include a concrete feature of the application and in
order to test if it works correctly they require a database connection, mailing systems, etc. Unit tests
are very fast and even there are options to allow them to execute in less time, they normally take
about 20-30 seconds to execute about 1,000 tests. Functional tests, as we repeated earlier on, require
connections to other system parts and they have more complexity, so execution becomes heavier.
The response time of this section is much higher.
As the time goes on and testing becomes more robust and solid, there are people who say that in
lots of situations, unit tests make no sense and just the functional part should be tested; on the other
hand there are people who think that testing an application 100% is a loss of time too. I don’t agree
with any of those statements. I did never agree the first of them but, the second one, conversly, there
was a time that I almost believe in it. Even so, the practice and the everyday use made me see that
a test may seem to be absurd but, tomorrow, that test can save you from a disaster. The reality is
that in lots of chances, investing time in testing a software 100% in a business world is non-viable
because of the date managed, but it’s undeniable that you should test absolutely everything (this is
a personal opinion) if you have time to do so. On the other hand, I found cases that, if I had omitted
the unit tests, details that are essential such as a correct balance, could have provoked a caos in the
application.
To end this introduction, and answering the first question, the yes with nuances is very bound to if
the application is already built and you have to maintain the code that was written by other person,
because testing that won’t be profitable and a lot of effort will be done for the work it will avoid us
to do. In those cases, it may be interesting to analyze and see the most critical parts and focus on
them, but this can vary depending on the concrete case. Other interesting solution could be, when
evolving the software, test the recently created code ensuring that the generated code since that date
is a clean code. Anyway, testing should exists as a basic premise in our future developments but not
as something optional or something guru related.
TDD vs BDD
These are acronyms that are being used more and more in development posts, documentations, best
practices or even job offers. I wrote a versus in the title but they are not antagonistic techniques. In
fact, BDD is a concrete interpetation of TDD based on the design.
TDD means Test Driven Development. This technique is based on developing unit tests, then build
the code that covers that unit test making it succeed and, finally, refactor the code.
BDD means Behaviour Driven Development. They are really similar techniques in their essence, the
most important different can be found in the concept itself. While in TDD tests must be created
before the code and then create the code so tests succeed, in BDD tests are design requirements or
specifications. The BDDism has another field more related to the software business logic and for
covering the different existing levels there are two subpractices: *storyBDD more focused on the
functional part and specBDD more focused on the unit testing part.
     Chapter 16: testing                                                                               137
     PHPUnit
     The well known PHP testing father. If you test or if you don’t, if you a PHP developer, you
     should have heard something about it. It’s a testing framework for PHP developer oriented. Most
     of the libraries and open-source frameworks use PHPUnit as a testing tool, such as Symfony. It’s
     a framework that belongs to the xUnit familly and, in order to check if a code is correct, it uses
     assertions.
     In order to use any testing tool, is essential to use a mocking library. In the PHP world you can find
     two different options that are above the rest of them: Mockery and Prophecy. Since PHPUnit 4.5,
     Prophecy is there by default.
     PHPUnit installation is very easy. Just execute the following Composer command:
     Here you have a basic example from the official website that can may help you to start using the
     tool for the first time:
     Production code:
 1   <?php
 2
 3   namespace LearnSymfony;
 4
 5   class Money
 6   {
 7       private $amount;
 8
 9        public function __construct($amount)
10        {
11            $this->amount = $amount;
12        }
13
14        public function getAmount()
15        {
16            return $this->amount;
     Chapter 16: testing                                                                            138
17        }
18
19        public function negate()
20        {
21            return new self(-1 * $this->amount);
22        }
23   }
The test:
 1   <?php
 2
 3   namespace LearnSymfony;
 4
 5   class MoneyTest extends \PHPUnit_Framework_TestCase
 6   {
 7       public function testCanBeNegated()
 8       {
 9           $a = new Money(1);
10           $b = $a->negate();
11           $this->assertEquals(-1, $b->getAmount());
12       }
13   }
     Once we have both the code and the test, you just need to execute vendor/bin/phpunit to get a
     result like this.
 1   $ vendor/bin/phpunit tests/
 2   PHPUnit 4.8.6 by Sebastian Bergmann and contributors.
 3   .
 4   Time: 655 ms, Memory: 4.25Mb
 5   OK (1 test, 1 assertion)
     PhpSpec
     It’s a much younger tool, created for a more bbd approach. The difference with PHPUnit, a xUnit
     tool, PhpSpec is a testing framework part of the xSpec family. This means that it’s more focused in
     the behaviour than in the code structure. We previously talked about Prophecy. Well, this mocking
     framework is developed by the same group of people that are behind the PhpSpec core, being a direct
     dependency of this tool.
     Chapter 16: testing                                                                             139
     Going back to something more theoric where some basic TDD and BDD features were explained,
     PhpSpec is a tool clearly focused to practise the BDD technique, but even more, inside the BDD,
     concretely the specBDD subpractice.
     The great input that this tool makes besides being a testing framework is that it works like a charm
     as a design tool because it has a powerful code generator that is improving with every version. The
     idea is that the test or the specification generates the base code thanks to some terminal questions
     prompted.
     As with PHPUnit, PhpSpec is also installed with Composer:
     Also, the same way we created the practical example with PHPUnit, here you have the base example
     of the official documentation:
     This file is a base structure of a xPec class in PhpSpec that can be autogenerated with the previous
     command.
 1   <?php
 2
 3   namespace spec;
 4
 5   use PhpSpec\ObjectBehavior;
 6   use Prophecy\Argument;
 7
 8   class MarkdownSpec extends ObjectBehavior
 9   {
10       function it_is_initializable()
11       {
12           $this->shouldHaveType('Markdown');
13       }
14   }
1   function it_converts_plain_text_to_html_paragraphs()
2   {
3       $this->toHtml("Hi, there")->shouldReturn("<p>Hi, there</p>");
4   }
1   vendor/bin/phpspec run
2   > spec\Markdown
3       � it converts plain text to html paragraphs
4           Class Markdown does not exist.
5               Do you want me to create it for you? [Y/n]
    PhpSpec notices that the Markdown class does not exists so it invites us to tell the tool to create the
    class for us. Execute the command again and this time the tool tells us that the toHtml method does
    not exist and it invites us again to create the method for us.
1   vendor/bin/phpspec run
2   > spec\Markdown
3     � it converts plain text to html paragraphs
4         Method Markdown::toHtml() not found.
5            Do you want me to create it for you? [Y/n]
This just creates our production code, without us having to code anything. Final result:
1   <?php
2
3   class Markdown
4   {
5       public function toHtml($argument1)
6       {
7           // TODO: write logic here
8       }
9   }
    If we now execute vendor/bind/phpspec run, an error will be launched as the one showed in the
    following block. It is saying that we should implement the toHtml method in order to satisfy the
    spec defined requirements:
     Chapter 16: testing                                                                                141
 1   > spec\Markdown
 2     � it converts plain text to html paragraphs
 3         Expected "<p>Hi, there</p>", but got null.
 4   1 examples (1 failed)
 5
 6   <?php
 7
 8   class Markdown
 9   {
10       public function toHtml()
11       {
12           return "<p>Hi, there</p>";
13       }
14   }
 1   vendor/bin/phpspec run
 2   > spec\Markdown
 3     � it converts plain text to html paragraphs
 4   1 examples (1 passed)
     Behat
     If PhpSpec is the best tool to do specBDD, then *Behat is the best tool for the **storyBDD* prac-
     tice. As we already know, the **storyBDD is at a higher level focusing on the business/functional
     scope.
     One of the most important limitations that is said anywhere when generating functional tests and
     I like to make clear that, in order to run this type of tests, you need an application, with its entry
     point, endpoint, front controller or whatever we want to call it, because without it, Behat cannot
     be executed. For example, if we follow the example that is being built during the whole book, there
     shouldn’t be any problem because it’s a Symfony web application with the front controller, in this
     case app.php or app_dev.php. However, if you create a new open-source bundle to be used by our
     future Symfony applications and you want to functional test that bundle, you should create your
     own bootstrap.php front controller and all the basic stack that makes a Symfony application work.
     Behat contains two different parts: the code part, with PHP classes with special Behat annotations
     that it knows how to interpet them as a concept called steps; and other part with Gherkin (a language
     similar to the human language) that generates scenarios that contain steps that connect with the
     mentioned PHP code.
     Exactly as with the previous tools, Behat is installed with Composer but, as we are required to install
     some dependencies apart from Behat, we recommend yo to copy and paste these four dependencies
     directly to the require-dev section of the composer.json file and then execute composer update.
     Chapter 16: testing                                                                            142
 1   "require-dev": {
 2       (...)
 3
 4          "behat/behat" :                                       "~3.0",
 5          "behat/symfony2-extension" :                          "~2.0",
 6          "behat/mink-extension" :                              "~2.0",
 7          "behat/mink-browserkit-driver":                       "~1.2"
 8   }
     Here you have a very simple example of how this storyBDD tool works. It’s not as easy to
     demonstrate as with the previous ones so we must show a more didactical and real example. If
     you want to go into detail, I suggest you to read this link⁹² from the official documentation.
     The PHP method that belongs to the CommandContext class that, as you can see, it has a @When
     annotation which is one of the reserved words that the Behat parser interprets and conects with the
     Gherkin written code.
 1   /**
 2     * @When /^I run "([^"]*)" interactive command$/
 3     */
 4   public function iRunInteractiveCommand($command)
 5   {
 6        $this->loadCommands();
 7        $command = $this->application->find($command);
 8        $this->tester = new CommandTester($command);
 9        $helper = $command->getHelper('dialog');
10        $helper->setInputStream($this->getInputStream('Test\\n'));
11        $this->inputs['command'] = $command->getName();
12        $this->tester->execute($this->inputs, $this->options);
13        $this->initialize();
14   }
     The following Gherkin code has not much to say about, not technical people can understand easily
     what every scenario resolves.
         ⁹²http://docs.behat.org/en/latest/quick_intro_pt1.html
     Chapter 16: testing                                                                            143
 1   @user
 2   Feature: Manage users via cli
 3     In order to manage users cli
 4     As a console
 5     I want to be able create a new user
 6
 7     Scenario: Executing command to create a new user
 8       Given the following command inputs:
 9         | email     | me@learnsymfony.com |
10         | username | learnSymfony         |
11         | firstName | Learn               |
12         | lastName | Symfony              |
13         | password | 123456               |
14       When I run "learnSymfony:user:create" interactive command
15       Then I should see the following output:
16       """
17         A new learnSymfony user has been created
18       """
     The following YAML code corresponds to the behat.yml configuration file in where some differente
     Behat options are defined, with folders and paths where contexts are located which are no more than
     PHP classes. It’s important to know the existence of the Behat\Symfony2Extension extension that
     gives some important details to Behat when it is used in a Symfony application.
 1   default:
 2       formatters:
 3            pretty:
 4                verbose: true
 5                paths: true
 6                snippets: false
 7       suites:
 8            user:
 9                paths:
10                    features: src/AppBundle
11                contexts:
12                    AppBundle\Behat\Context\CommandContext
13                filters:
14                    tags: "@user"
15       extensions:
16            Behat\MinkExtension:
17                sessions:
18                    default:
     Chapter 16: testing                                                                               144
19                          symfony2: ~
20                  browser_name: firefox
21                  show_auto: false
22              Behat\Symfony2Extension: ~
     Lastly, we can launch Behat as if we were launching PHPUnit or PhpSpec. Execute the following
     command:
1 vendor/bin/behat
     Continuous integration
            Continuous Integration is a software development practice where members of a team
            integrate their work frequently, usually each person integrates at least daily - leading
            to multiple integrations per day. Each integration is verified by an automated build
            (including test) to detect integration errors as quickly as possible.
     That sentence is the way Martin Fowler defines the continuous integration concept. It’s a way of
     development that implies automatic continuous deployments as a way of finding possible errors in
     an earlier development stage.
     In the next sections, I will show you two of the more consolidated alternatives existing in the
     integration tools family: Jenkins and Travis.
     Jenkins
     If there is a software directly associated to the continuous integration concept that is Jekins. It’s
     written in JAVA and it runs in a servlet container servers, such as Apache Tomcat. It was firstly
     developed with a different name, Hudson, and it’s two big secrets for the success have been: being
     the first ones (and open-source) and it’s plugin based architecture. This has done that, nowadays,
     Jenkins has a lot of plugins for almost everything.
     Travis
     Another alternative, much newer and conceptually very different: Travis. It is the king of continuous
     integration for open-source projects, libraries, etc. Great part of the code hosted on GitHub as open
     source that uses continuous integration use Travis. It’s simplicity has been a key factor for the
     success, because you have to download and install Jenkins in your systems, and configure a lot
     of different options, Travis starts working with just four easy configurations in a YAML file as if it
     was magic, making continuous integration quick in this kind of repositories. I won’t talk here about
     Travis configuration because in this chapter tip, you will find all the information you need.
     Chapter 16: testing                                                                                145
     Tip 16
     In the previous section, we talked about continuous integration explaining quickly and easily what
     it is and two of the most used tools for the PHP and the rest of the development communities.
     Well, in this tip we are going to explain in easily how to configure a .travis.yml file so our favorite
     continuous integration server reports us our Behat and PhpSpec tests with green and red colors in
     that build of our Symfony application, allowing us (or not allowing) to deploy.
 1   language: php
 2   # it tells Travis the language we are using.
 3
 4   php:
 5       -   5.4
 6       -   5.5
 7       -   5.6
 8       -   7.0
 9       -   hhvm
10   # the   different environments the build must be excecuted
11
12   env:
13       - SYMFONY_VERSION=2.3.*
14       - SYMFONY_VERSION=2.7.*
15       - SYMFONY_VERSION=dev-master
16   # shows the different Symfony versions in where it must be executed
17   # the application build
18
19   before_install:
20       - sudo apt-get update
21       - sudo apt-get -qq install ruby-sass
22   # Travis use this block to execute commands before the environment
23   # installation. Here we update Ubuntu packages and we install Sass
24
25   install:
26       - sudo apt-get install apache2 libapache2-mod-fastcgi
27       - sudo a2enmod rewrite actions fastcgi alias
28       - echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/e\
29   tc/php.ini
30       - sudo cp -f scripts/travis/vhost /etc/apache2/sites-available/default
31       - sudo sed -e "s?%TRAVIS_BUILD_DIR%?$(pwd)?g" --in-place /etc/apache2/sites-\
32   available/default
33       - sudo service apache2 restart
34       - cat /etc/apache2/sites-available/default
     Chapter 16: testing                                                              146
Summary
In this chapter we have seen a short but intense introduction to the surrounding testing world from
a Symfony point of view. We have analized the different testing practices and their main PHP tools;
we have briefly explained some concepts about continuous integration seeing the two most famous
alternatives: Jenkins and Travis. Lastly, in the tip, we took advantage of the open-source aim of
this book, we showed a complete .travis.yml of a Symfony application. The goal here was to show
an starting point so you can follow learning something as important as testing. This book is not
about testing but, if you are willing to read more about it, you can read a book written by Fernando
Arconada in spanish called Testing for Symfony2 Applications⁹³.
  ⁹³https://leanpub.com/testingsymfony2
Epilogue
Our journey come to its end after more than 150 intense pages full of code and, specially, desire to
learn. This is just the “advanced” starting of something much bigger, but we will go step by step
because the most important thing right now is securing the actual knowledge master them. It’s
possible that I write about more technologies and other ways of working in future books, ways that
nowadays professionals use daily, but I will like to say goodbye with some of the technologies I use
and I consider interesting so you check them out.
Bower
This is commonly named as a “must have”… or at least it was. It’s a package manager for the
web. And you are thinking… another one? In fact, another one, but this one is commonly used
for front libraries: jQuery, Foundation, slick carousel, etc. With the bower.json file you will be able
to establish the front dependencies that you want in the version you need them, and thanks to the
.bowerrc file you will be able to download the desired libraries in the path you want (you better
put them inside the app/Resources/assets/vendor/ folder or I will get angry). You won’t need to
download the libraries manually again and checking for upgrade versions in not very friendly pages.
Be careful: you can also use npm for front libraries too.
Gassetic
Assetic is the tool that I told you not to use but we did use it to didactically see how it works. A lot of
people will tell you the same as me: use a task tool to do this action. However, there are people who
still have a special affection for Assetic and that’s why they created Gassetic⁹⁴. I haven’t checked
how it works so, use it in your project and tell me how it is performing so I can add a little section
to this book.
PostCSS
PostCSS is just an API with lots of plugins that use this API to make a lot of stuff. Don’t look at the
technology name because it’s full of powerful abilites, even preprocessing ones. As I’m writing these
lines, a lot of people is write more and more PostCSS modules such as Autoprefixer, cssnext, grid,
… Almost anything you can think about CSS precessing and postprocessing. It’s also the quickest
of the nowadays solutions, and that why a lot of people is changing from Sass to PostCSS. You can
browse some of the plugins here⁹⁵
DDD
We already talked about TDD and BDD, but you can’t finish this book without reasearching about
DDD. Without any doubts, if you want to become a great developer, everything that surrounds DDD
and design patterns should be familiar for you. This is going to be a hard work, but the resulting
scene won’t leave you indifferent.
Thanks to readers
The most important part of this book is you, so thank you for your time reading this book, I will
express my gratitude if I receive some feedback about it, even the good or the bad one. Thanks to
the reader for being with me, for spending your precious time with me behind these lines. I hope
that thanks to them you became a better developer.