Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
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!

48
Login or Register to join the conversation
Vladimir Z. Avatar
Vladimir Z. Avatar Vladimir Z. | posted 4 years ago

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?

1 Reply

Hey Vladimir Z.

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!

Reply
Eduard Avatar

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.

Reply

Hey Eduard

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!

Reply
Dirk H. Avatar
Dirk H. Avatar Dirk H. | posted 2 years ago

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

Reply

Hey Dirk H.

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

Cheers!

Reply
Daniel R. Avatar
Daniel R. Avatar Daniel R. | posted 2 years ago

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 : )

Reply

Hello Daniel R.

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!

Reply
Daniel R. Avatar

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!

Reply

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

Reply

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!

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 3 years ago

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.

Reply

Hey Dmitriy

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!

Reply
Dmitriy Avatar

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?

Reply

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

1 Reply
Dmitriy Avatar

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.

Reply

Hey Dmitriy!

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!

Reply
Dmitriy Avatar

Hey Ryan!

Excellent answer! I understood.

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

Thank you very much!

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 3 years ago

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?

Reply

Hey Dmitriy

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

Cheers!

Reply
John christensen Avatar
John christensen Avatar John christensen | posted 3 years ago

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

Reply
John christensen Avatar

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

Reply

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!

Reply

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?!!

Reply

Hey @Kenan

You forgot -t public parameter =)

Cheers!

Reply
Dominik Avatar
Dominik Avatar Dominik | posted 4 years ago

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 :)

Reply

Hi Dominik

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!

Reply
Dominik Avatar

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>

Reply

Hey Dominik, 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!

Reply
Dominik Avatar

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.

Reply

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

Reply
Dominik Avatar

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

Reply

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

Reply
Dominik Avatar

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

Reply

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

Reply
Dominik Avatar

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

Thank you for help.
Regards

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 4 years ago

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?

Reply

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!

Reply
Peter K. Avatar
Peter K. Avatar Peter K. | posted 4 years ago

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?

Reply
Default user avatar

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.

1 Reply

Good explanation :)

Reply

Hey Peter K.

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!

Reply
Kribo Avatar

How does one create a reusable bundle in symfony 4.0?

Reply
victor Avatar victor | SFCASTS | Kribo | posted 4 years ago | edited

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!

Reply
Abelardo Avatar
Abelardo Avatar Abelardo | posted 3 years ago

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.

-1 Reply
Abelardo Avatar

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!

1 Reply
Abelardo Avatar

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

Reply
Abelardo Avatar

Hey Abelardo,

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

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.1.4
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.0.2
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}