This tutorial has a new version, check it out!

Bundles give you Services

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Yo guys! Welcome to episode 2 of our Symfony 4 series! This is a very important episode because we're going to take just a little bit of time to really understand how our app works. I'm talking about how configuration works, the significance of different files and a whole lot more.

And yea, there's a reason we're doing this work now and not in a distant, future episode: by understanding a few FUNdamentals, everything else in Symfony will make a lot more sense. So let's dig in and get to work!

Code Download

As always, if you code along with me, we instantly become best friends. Pow! Download the course code from this page and unzip it. Inside, you'll find a start/ directory with the same code you see here.

Open the README.md file for a whimsical space poem... and instructions on how to get the app setup. The last step will be to find a terminal, move into the project and run:

./bin/console server:run

to start the built-in PHP web server. Ok! Let's load up our app! Find your browser and go to http://localhost:8000. Welcome back to... "The Space Bar": the latest and greatest intergalactic news and sharing site for astronauts and non-human-eating aliens across the universe. Or, it will be when we're finished.

Services: Objects that do Work

Let's start off stage 2 of our journey with a pop quiz: in episode 1, what did I say was the most important part of Symfony? If you answered Fabien... you're technically right, but the real answer is: services. Remember: a service is an object that does work: there's a logger service and a Twig service.

To get a list of the services that we can access, you can go to your terminal, open a new tab, and run:

./bin/console debug:autowiring

For example, to get the logger service, we can use the LoggerInterface type-hint:

... lines 1 - 4
use Psr\Log\LoggerInterface;
... lines 6 - 11
class ArticleController extends AbstractController
{
... lines 14 - 39
/**
* @Route("/news/{slug}/heart", name="article_toggle_heart", methods={"POST"})
*/
public function toggleArticleHeart($slug, LoggerInterface $logger)
{
... lines 45 - 46
$logger->info('Article is being hearted!');
... lines 48 - 49
}
}

You can see this in our controller: yep, as soon as we add an argument with the LoggerInterface type-hint, Symfony knows to pass us the logger service.

Where do Services Come From? Bundles

But... where do these service objects come from? I mean, somebody must be creating them in the background for us, right? Totally! It's not very important yet, but every service is stored inside another object called the container. And each service has an internal name, just like routes.

And what exactly puts these services into the container? The answer: bundles. Bundles are Symfony's plugin system. Look inside config/ and open a bundles.php file there:

... lines 1 - 2
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
];

Yep, our app has seven bundles so far - basically seven plugins. We installed 6 of these in episode 1: the recipe system automatically updates this file when you require a bundle. Sweet!

So let's put this all together: Symfony is really nothing more than a collection of services. And bundles are what actually prepare those service objects and put them into the container. For example, MonologBundle is responsible for giving us the logger service.

Bundles can also do other things - like add routes. But they really have one main job: bundles give you services. If you add a bundle, you get more services. And remember, services are tools.

So let's install a new bundle and play with some new tools!

Leave a comment!

  • 2020-06-23 Vladimir Sadicov

    Hey Ecludio

    Which exactly problems do you have with php 7.4? We know that there are some incompatibilities, but we always trying to resolve them.

    However about upgrading to 4.4... sorry but we can't do that. It will be upgraded across the courses, and everything written in this tutorial is valid for newer versions of Symfony, Also this tutorial already has a newer version which is based on Symfony 5.

    Cheers!

  • 2020-06-21 Ecludio

    There are a few problems related to outdated stuff in the sourcecode of this tutorial.
    Please update the project to symfony 4.4 and php7.4.
    I got it done myself reading the docs but some people wont.

  • 2020-04-13 Diego Aguiar

    Hey Dirk

    Actually, you only have to update Symfony Flex. You can check the related issue about it here: https://github.com/symfony/...

    Cheers!

  • 2020-04-12 Dirk

    If you have problem with PHP 7.4 and you get the error message "curl_multi_setopt(): CURLPIPE_HTTP1 is no longer supported".

    Then you have to execute composer global require symfony/flex ^1.5

  • 2019-12-26 Diego Aguiar

    Oh, yes, that changed I believe before Symfony 4.2, it used to read the .env.dist file instead of .env

  • 2019-12-24 DannyR

    Thank you Diego. I got it working. I could not find a .env.dist file in the unzipped project code. I do see a .env.dist file in the https://github.com/knpunive... repository, but it does not appear in the downloaded zip file. I recreated a local version of this file by copy and pasting the contents from the github repository. It turns out that I needed to keep the file name as ".env.dist" for it to work (at least it doesn't give me an error). I don't know why this file could not be downloaded. And I'm not sure why ".env.dist" works in my setup without needing to rename it as you advise. Perhaps it's because of the Symfony version I'm using. Cheers!

  • 2019-12-23 Diego Aguiar

    Hello DannyR

    Before anything, the start directory of the first, second, third, etc chapter contains the same code. We only deliver the start and finish folder of the whole course.
    About your problem,it looks like you are missing the .env file from your project, and that's because the course code comes with a .env.dist file (the reason of that change comes from a Symfony kind of recent change). You only have to rename that file to .env and then it should be working. Please, try doing that and if there is anything still wrong let me know :)

    Cheers!

  • 2019-12-23 DannyR

    Hello.

    Newbie here--I'm having difficulty moving from the first episode setup to the second episode setup. In PHPStorm I deleted the contents of first episode the_spacebar directory and pasted the contents of the start directory from the second episode course code. When I run composer install in the MacOS terminal, it successfully runs 46 installs but at the bottom I get the message:

    Package symfony/lts is abandoned, you should avoid using it. Use symfony/flex instead.
    Generating autoload files
    Executing script cache:clear [KO]
    [KO]
    Script cache:clear returned with error code 255
    !!
    !! Fatal error: Uncaught Symfony\Component\Dotenv\Exception\PathException: Unable to read the "/Users/roushd/the_spacebar/bin/../.env" environment file. in /Users/roushd/the_spacebar/vendor/symfony/dotenv/Dotenv.php:54
    !! Stack trace:
    !! #0 /Users/roushd/the_spacebar/bin/console(22): Symfony\Component\Dotenv\Dotenv->load('/Users/roushd/t...')
    !! #1 {main}
    !! thrown in /Users/roushd/the_spacebar/vendor/symfony/dotenv/Dotenv.php on line 54
    !!
    Script @auto-scripts was called via post-install-cmd

    When I tried to run php composer.phar install, I get:

    Could not open input file: composer.phar

    When I run php bin/console server:run, I get:

    Fatal error: Uncaught Symfony\Component\Dotenv\Exception\PathException: Unable to read the "/Users/roushd/the_spacebar/bin/../.env" environment file. in /Users/roushd/the_spacebar/vendor/symfony/dotenv/Dotenv.php:54
    Stack trace:
    #0 /Users/roushd/the_spacebar/bin/console(22): Symfony\Component\Dotenv\Dotenv->load('/Users/roushd/t...')
    #1 {main}
    thrown in /Users/roushd/the_spacebar/vendor/symfony/dotenv/Dotenv.php on line 54

    Please help : )

  • 2019-09-17 Victor Bocharsky

    Hey Max,

    Try to upgrade the Security Checker package in your application. If it does not help, you can remove it from the Composer scripts in composer.json file and run the command manually.

    Cheers!

  • 2019-09-07 Дмитрий Ченгаев

    Hey Ryan!

    Excellent answer! I understood.

    The folder Resources/config/monolog.xml this is what I was looking for.

    Thank you very much!

  • 2019-09-07 weaverryan

    Hey Дмитрий Ченгаев!

    Excellent question! When we type-hint Psr\Log\LoggerInterface for an argument that we want autowired, the autowiring system *simply* looks in the container for a service whose *id* is Psr\Log\LoggerInterface. So then... how/why does that type-hint *actually* give us a service whose id is "monolog.logger"? Because the MonologBundle adds a service *alias* from Psr\Log\LoggerInterface to monolog.logger.

    Let me say it in a different way :).

    1) MonologBundle registers a service whose id is monolog.logger and whose class is Symfony\Bridge\Monolog\Logger. It does this via an XML file, but it's not so unlike your services.yaml file: https://github.com/symfony/...

    2) The bundle also registers *other* logger services (as you mentioned) with our service ids.

    If the bundle stopped here, autowiring would NOT work if you type-hinted an argument with Psr\Log\LoggerInterface? Why? Because there would be no service in the container whose id is Psr\Log\LoggerInterface. So *enable* autowiring, the bundle does a third thing:

    3) The bundle creates a service "alias" from Psr\Log\LoggerInterface to monolog.logger. This means there *is* now a service in the container whose id is Psr\Log\LoggerInterface... but it really gives you the monolog.logger service. This enables autowiring. Where is that defined? Right here: https://github.com/symfony/... . (actually, this aliases to a service whose id is "logger" but if you look at the line above this, "logger" is then aliased to "monolog.logger").

    Phew! I hope that was the deep, dark explanation you were hoping for ;). Let me know!

    Cheers!

  • 2019-09-07 Дмитрий Ченгаев

    For example, Psr\Log\LoggerInterface connected to Symfony\Bridge\Monolog\Logger with service id monolog.logger. Where can I find the code that performs this connection in the file service.yaml it is not.

  • 2019-09-06 Diego Aguiar

    Are you talking about autowiring a class from a third party *non-bundle* library? Or what you mean with "if such code is not in the file services.yaml"?

  • 2019-09-05 Дмитрий Ченгаев

    Thanks Diego! Command php bin/console debug:container [service_name] helped me. But, where technically there is a connection of the service with the interface, if such code is not in the file services.yaml?

  • 2019-09-05 Diego Aguiar

    Hey Дмитрий Ченгаев

    That's a good question and Symfony has a couple of commands to help you out.
    First you run php bin/console debug:autowiring [ClassName], you can pass in the FQCN or just a part of the name, like, Serializer. You will see a list of classes and interfaces that matches your term aside with the name of the service being autowired.
    Once you locate the service name you are looking for, then you can run php bin/console debug:container [service_name], and that's it! You will get information about that service

    Cheers!

  • 2019-09-05 Дмитрий Ченгаев

    There are multiple services in the container that implement ...Interface, such as service1, service2, ... How to know which service will be automatically selected by Symfony for this Interface? In service.yaml nothing is indicated about it.

  • 2019-08-21 Diego Aguiar

    Hey Дмитрий Ченгаев

    Have you tried using ZenstruckRedirectBundle (https://github.com/kbond/Ze... I think it does exactly what you need

    Cheers!

  • 2019-08-21 Дмитрий Ченгаев

    I have a blog on Symfony 4. Some pages of this blog change their URL address and I need to make a 301 redirect from the old page address to the new one. How to do it right at Symfony 4? Maybe there is some kind of bundle?

  • 2019-07-22 Victor Bocharsky

    Hey John,

    I'm glad you were able to solve this issue. And thank you for sharing your solution with others! Unfortunately, our instructions in README are only valid if you code from start/ or finish/ directories, if you copy some code from those folder to your own project - it does not cover those cases and you probably need to handle them by yourself - we just don't want to mislead our users with more instructions that might be not related to their case.

    Cheers!

  • 2019-07-19 John Christensen

    Nvmd.. I apparently started this project by copying the contents of the 'start' folder rather than the entire folder. So two invisible files (.env.dist and .gitignore) did not copy over. My bad :(

    But I still think the README.md file should remind the user to copy .env.dist and rename it to .env

  • 2019-07-19 John Christensen

    I started this tutorial with downloaded source. But after running composer install, I get the following error:

    Script cache:clear returned with error code 255

    To get it to work, I had to add my own .env file (at the project root) with this content:

    ###> symfony/framework-bundle ###
    APP_ENV=dev
    APP_SECRET=f81422bf04497ffca0d1abc0b7125211
    #TRUSTED_PROXIES=127.0.0.1,127.0.0.2
    #TRUSTED_HOSTS='^localhost|example\.com$'
    ###< symfony/framework-bundle ###

    Did I miss something? Or should this step be added to the README.md?

    And btw, the Symfony version installed was 4.0.14

  • 2019-05-17 Vladimir Sadicov

    Hey @Kenan

    You forgot -t public parameter =)

    Cheers!

  • 2019-05-16 Kenan

    Hi!

    Using "php -S localhost:8000" return "The requested resource / was not found on this server."
    BUT "php bin/console server:run" just work fine.

    What is the Idea? .. What I missed?!!

  • 2018-12-21 Victor Bocharsky

    Hey Abelardo,

    No problem! I'm glad we don't have any issues on our side.

    Cheers!

  • 2018-12-21 AbelardoLG

    My fault!
    I was wrong with my last comment since I bought another course and not this one!
    Sorry for the inconveniences. Best regards.

  • 2018-12-21 Victor Bocharsky

    Hey Abelardo,

    We're sorry about that! Such requests you better write directly to our email because it's not a good idea to post important credentials in comments. Btw, I've already replied to your previous email about this problem to clarify some things first and waiting feedback from you.

    Cheers!

  • 2018-12-21 AbelardoLG

    Hi everyone!

    I paid for this course but I can't download the code for it.

    Could anybody help me with this issue, please?

    Best regards.

  • 2018-12-05 Usuri

    Success.:) I didn't realised there is no .htaccess in public folder. I added it manually and everything works. :)

    Thank you for help.
    Regards

  • 2018-12-05 Vladimir Sadicov

    Now I think we need to remove previous changes. and best solution for you will be to run

    composer require symfony/apache-pack

    It will add default .htaccess file and I think it would work after it

  • 2018-12-05 Usuri

    It works. So what should I change in mod_rewrite?
    I checked for url "kurssymfony.lok/index.php" and then debug toolbar works.

  • 2018-12-05 Vladimir Sadicov

    This looks good. Now I see it's mod_rewrite error

    Try to open

    http://<host>/index.php/_profiler/latest?panel=request

    if it works, than it's 100% problem in rewrite rules

  • 2018-12-05 Usuri

    Apache error log contains this:

    "[Wed Dec 05 09:34:14.784030 2018] [ssl:warn] [pid 20324:tid 360] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:14.828072 2018] [core:warn] [pid 20324:tid 360] AH00098: pid file C:/xampp/apache/logs/httpd.pid overwritten -- Unclean shutdown of previous Apache run?
    [Wed Dec 05 09:34:14.830074 2018] [ssl:warn] [pid 20324:tid 360] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:14.855098 2018] [mpm_winnt:notice] [pid 20324:tid 360] AH00455: Apache/2.4.29 (Win32) OpenSSL/1.1.0g PHP/7.2.0 configured -- resuming normal operations
    [Wed Dec 05 09:34:14.855098 2018] [mpm_winnt:notice] [pid 20324:tid 360] AH00456: Apache Lounge VC15 Server built: Nov 3 2017 10:30:36
    [Wed Dec 05 09:34:14.855098 2018] [core:notice] [pid 20324:tid 360] AH00094: Command line: 'c:\\xampp\\apache\\bin\\httpd.exe -d C:/xampp/apache'
    [Wed Dec 05 09:34:14.856099 2018] [mpm_winnt:notice] [pid 20324:tid 360] AH00418: Parent: Created child process 10848
    AH00548: NameVirtualHost has no effect and will be removed in the next release C:/xampp/apache/conf/extra/httpd-vhosts.conf:44
    [Wed Dec 05 09:34:15.271498 2018] [ssl:warn] [pid 10848:tid 460] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:15.309534 2018] [ssl:warn] [pid 10848:tid 460] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:15.335559 2018] [mpm_winnt:notice] [pid 10848:tid 460] AH00354: Child: Starting 150 worker threads."

    Browser network log shows "404 - object not found".

  • 2018-12-05 Vladimir Sadicov

    You can check it in error log configured in your xampp instance and Network panel of your browser dev tools.

  • 2018-12-05 Usuri

    Now debug bar appeared but only with error message: "An error occurred while loading the web debug toolbar." Is there any log where I can check what is wrong wit toolbar?

    BTW. Similar debug toolbar for laravel works without problems.

  • 2018-12-04 Vladimir Sadicov

    Hey Usuri, I think you need to enable apache mod_rewrite and add to your config into Directory block this code or something like this:


    <ifmodule mod_rewrite.c="">
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
    </ifmodule>


    this is just example it could be little different depending on your apache version.

    Hope this will help

    Cheers!

  • 2018-12-04 Usuri

    Thank you.

    This is my config in "httpd-vhosts.conf":

    <virtualhost *:80="">
    DocumentRoot "C:/xampp/htdocs/skeleton_symfony/public"
    ServerName kurssymfony.lok

    <directory "c:="" xampp="" htdocs="" skeleton_symfony="" public"="">
    Options Indexes FollowSymLinks Includes ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
    </directory>
    </virtualhost>

  • 2018-12-04 Vladimir Sadicov

    Hi Usuri

    It should work no matter what are you using symfony server or xampp or other server. It looks like your virtual host are not fully configured. If you share it with us we can try to look together and fix everything.

    Cheers!

  • 2018-12-04 Usuri

    Hi.
    I have a small question about technical issue. On my windows system I use XAMPP and configure virtual host for localhosts domains. So I created virtual host for this tutorial with local domain address "symfonytut.loc" and it works. But site doesn't shows me debug bottom bar on page. If I run symfony web server (php bin/console server:run) debug bar appeared. Is there any chance to see that debug bar when use virtual host?
    Thank you
    BTW. Great tutorial :)

  • 2018-08-06 Victor Bocharsky

    Hey Mike,

    Good question, let's figure out! Actually, we didn't install it explicitly, but it was installed as a dependency. Let me show you, we did install "symfony/debug-pack" of version v1.0.4 that requires "symfony/phpunit-bridge" which in turns put that phpunit.xml.dist file for us. But this extra dependency was removed in the newer version v.1.0.5, so if you install the newer version of "symfony/debug-pack" - you won't have phpunit.xml.dist file, i.e. you need to require "symfony/phpunit-bridge" explicitly, see https://packagist.org/packa...

    Cheers!

  • 2018-08-04 Mike

    In the main directory of this app is a phpunit.xml.dist file, which wasnt available in the first chapter. How have you "installed" PHPUnit for this project?

  • 2018-06-06 Diego Aguiar

    Good explanation :)

  • 2018-06-06 toporovvv

    Your controller extends AbstractController (/vendor/symfony/framework-bundle/Controller/AbstractController.php), which uses ControllerTrait (/vendor/symfony/framework-bundle/Controller/ControllerTrait.php). So render(), redirect(), getDoctrine(), getUser() and lots of other useful functions, that we use often in our actions, live there.

    You can also find method getSubscribedServices() in AbstractController which allow you to understand what services do we have out of the box in every action. Other services you need to inject via constructor or methods parameters as Ryan said in a first course of this track.

  • 2018-06-04 Diego Aguiar

    Hey Peter Kosak

    Calling "$this->render()" works because your controller extends from Symfony's base controller, in that class, you can see the implementation of the method, In theory, all your services must be private, and you should use dependency injection in order to use them, but, there are still some public services, like Twig, that you can fetch them out from the container.

    I hope it helps you clarifying things a bit :)
    Cheers!

  • 2018-06-04 Peter Kosak

    This is probably stupid question but I am still thinking about it.

    Why do we need to inject LoggerInterface (service) into route to get its methods but we are not injecting Twig (environment service).

    Basically my question is how is that possible that when I call $this->render() it knows that it is a twig method so it is looking in Environemt service but when I call $this->info() it is failing?

    I try it again because it is not clear:
    Why we are not injecting into each route Environment but we have to inject Logger?

    Where is this specified what services have to be injected and which one does not have to be?

  • 2018-04-13 Diego Aguiar

    Hey Vlad

    Some times we do a couple of changes from course to course, but usually we mention that. I believe, this time you can safely continue with your project from the past course

    Cheers!

  • 2018-04-13 Vlad

    Is the start source code of this tutorial builds on the finish source code from the previous tutorial or there have been changes in between?

  • 2018-02-14 Victor Bocharsky

    Hey kribo ,

    Well, the process is the same as for Symfony 3.x. Well, actually, you can look at any third-party Symfony bundle, like https://github.com/FriendsO... and steal the skeleton from it. Also, Flex can make installing your bundle more smooth if you will create a recipe for it and publish it here: https://symfony.sh/ - it has a link about how to create your own recipe.

    Cheers!

  • 2018-02-13 kribo

    How does one create a reusable bundle in symfony 4.0?