Chapters
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
Bundles give you Services
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
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:
Show Lines
|
// ... lines 1 - 4 |
use Psr\Log\LoggerInterface; | |
Show Lines
|
// ... lines 6 - 11 |
class ArticleController extends AbstractController | |
{ | |
Show Lines
|
// ... lines 14 - 39 |
/** | |
* @Route("/news/{slug}/heart", name="article_toggle_heart", methods={"POST"}) | |
*/ | |
public function toggleArticleHeart($slug, LoggerInterface $logger) | |
{ | |
Show Lines
|
// ... lines 45 - 46 |
$logger->info('Article is being hearted!'); | |
Show Lines
|
// ... 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:
Show Lines
|
// ... 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!
48 Comments
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!
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.
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!
If you have problem with PHP 7.4 and you get the error message "<b>curl_multi_setopt(): CURLPIPE_HTTP1 is no longer supported</b>".
Then you have to execute composer global require symfony/flex ^1.5
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!
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 : )
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!
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!
Oh, yes, that changed I believe before Symfony 4.2, it used to read the .env.dist file instead of .env
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!
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.
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!
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?
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
"?
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.
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!
Hey Ryan!
Excellent answer! I understood.
The folder Resources/config/monolog.xml this is what I was looking for.
Thank you very much!
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?
Hey Dmitriy
Have you tried using ZenstruckRedirectBundle (https://github.com/kbond/Ze... I think it does exactly what you need
Cheers!
I started this tutorial with downloaded source. But after running composer install
, I get the following error:
<blockquote>Script cache:clear returned with error code 255</blockquote>To get it to work, I had to add my own .env file (at the project root) with this content:
###> symfony/framework-bundle ###<br />APP_ENV=dev<br />APP_SECRET=f81422bf04497ffca0d1abc0b7125211<br />#TRUSTED_PROXIES=127.0.0.1,127.0.0.2<br />#TRUSTED_HOSTS='^localhost|example\.com$'<br />###< 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
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
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!
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?!!
Hey @Kenan
You forgot -t public
parameter =)
Cheers!
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 :)
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!
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>
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!
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.
You can check it in error log configured in your xampp instance and Network panel of your browser dev tools.
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".
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
It works. So what should I change in mod_rewrite?
I checked for url "kurssymfony.lok/index.php" and then debug toolbar works.
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
Success.:) I didn't realised there is no .htaccess in public folder. I added it manually and everything works. :)
Thank you for help.
Regards
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?
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!
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?
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.
Good explanation :)
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!
How does one create a reusable bundle in symfony 4.0?
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!
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.
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!
My fault!
I was wrong with my last comment since I bought another course and not this one!
Sorry for the inconveniences. Best regards.
Hey Abelardo,
No problem! I'm glad we don't have any issues on our side.
Cheers!
"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.21.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/stopwatch": "^3.3|^4.0", // v4.0.4
"symfony/var-dumper": "^3.3|^4.0", // v4.0.4
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.0.4
}
}
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?