gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
It's time to talk about the most fundamental part of Symfony: services!
Honestly, Symfony is nothing more than a bunch of useful objects that work together. For example, there's a router object that matches routes and generates URLs. There's a Twig object that renders templates. And there's a Logger object that Symfony is already using internally to store things in a var/log/dev.log
file.
Actually, everything in Symfony - I mean everything - is done by one of these useful objects. And these useful objects have a special name: services.
But don't get too excited about that word - service. It's a special word for a really simple idea: a service is any object that does work, like generating URLs, sending emails or saving things to a database.
Symfony comes with a huge number of services, and I want you to think of services as your tools.
Like, if I gave you the logger service, or object, then you could use it to log messages. If I gave you a mailer service, you could send some emails! Tools!
The entire second half of Symfony is all about learning where to find these services and how to use them. Every time you learn about a new service, you get a new tool, and become just a little bit more dangerous!
Let's check out the logging system. Find your terminal and run:
tail -f var/log/dev.log
I'll clear the screen. Now, refresh the page, and move back. Awesome! This proves that Symfony has some sort of logging system. And since everything is done by a service, there must be a logger object. So here's the question: how can we get the logger service so that we can log our own messages?
Here's the answer: inside the controller, on the method, add an additional argument. Give it a LoggerInterface
type hint - hit tab to auto-complete that and call it whatever you want, how about $logger
:
... lines 1 - 4 | |
use Psr\Log\LoggerInterface; | |
... lines 6 - 10 | |
class ArticleController extends AbstractController | |
{ | |
... lines 13 - 41 | |
public function toggleArticleHeart($slug, LoggerInterface $logger) | |
{ | |
... lines 44 - 48 | |
} | |
} |
Remember: when you autocomplete, PhpStorm adds the use
statement to the top for you.
Now, we can use one of its methods: $logger->info('Article is being hearted')
:
... lines 1 - 10 | |
class ArticleController extends AbstractController | |
{ | |
... lines 13 - 41 | |
public function toggleArticleHeart($slug, LoggerInterface $logger) | |
{ | |
// TODO - actually heart/unheart the article! | |
$logger->info('Article is being hearted!'); | |
... lines 47 - 48 | |
} | |
} |
Before we talk about this, let's try it! Find your browser and click the heart. That hit the AJAX endpoint. Go back to the terminal. Yes! There it is at the bottom. Hit Ctrl
+C
to exit tail
.
Ok cool! But... how the heck did that work? Here's the deal: before Symfony executes our controller, it looks at each argument. For simple arguments like $slug
, it passes us the wildcard value from the router:
... lines 1 - 10 | |
class ArticleController extends AbstractController | |
{ | |
... lines 13 - 38 | |
/** | |
* @Route("/news/{slug}/heart", name="article_toggle_heart", methods={"POST"}) | |
*/ | |
public function toggleArticleHeart($slug, LoggerInterface $logger) | |
{ | |
... lines 44 - 48 | |
} | |
} |
But for $logger
, it looks at the type-hint and realizes that we want Symfony to pass us the logger object. Oh, and the order of the arguments does not matter.
This is a very powerful idea called autowiring: if you need a service object, you just need to know the correct type-hint to use! So... how the heck did I know to use LoggerInterface
? Well, of course, if you look at the official Symfony docs about the logger, it'll tell you. But, there's a cooler way.
Go to your terminal and run:
./bin/console debug:autowiring
Boom! This is a full list of all of the type-hints that you can use to get a service. Notice that most of them say that they are an alias to something. Don't worry about that too much: like routes, each service has an internal name you can use to reference it. We'll learn more about that later. Oh, and whenever you install a new package, you'll get more and more services in this list. More tools!
And check this out! If you want to get the Twig service, you can use either of these two type-hints.
And remember how I said that everything in Symfony is done by a service? Well, when we call $this->render()
in a controller, that's just a shortcut to fetch the Twig service and call a method on it:
... lines 1 - 10 | |
class ArticleController extends AbstractController | |
{ | |
... lines 13 - 23 | |
public function show($slug) | |
{ | |
... lines 26 - 31 | |
return $this->render('article/show.html.twig', [ | |
... lines 33 - 35 | |
]); | |
} | |
... lines 38 - 49 | |
} |
In fact, let's pretend that the $this->render()
shortcut does not exist. How could we render a template? No problem: we just need the Twig service. Add a second argument with an Environment
type-hint, because that's the class name we saw in debug:autowiring
. Call the arg $twigEnvironment
:
... lines 1 - 9 | |
use Twig\Environment; | |
class ArticleController extends AbstractController | |
{ | |
... lines 14 - 24 | |
public function show($slug, Environment $twigEnvironment) | |
{ | |
... lines 27 - 39 | |
} | |
... lines 41 - 52 | |
} |
Next, change the return
statement to be $html = $twigEnvironment->render()
:
... lines 1 - 9 | |
use Twig\Environment; | |
class ArticleController extends AbstractController | |
{ | |
... lines 14 - 24 | |
public function show($slug, Environment $twigEnvironment) | |
{ | |
... lines 27 - 32 | |
$html = $twigEnvironment->render('article/show.html.twig', [ | |
'title' => ucwords(str_replace('-', ' ', $slug)), | |
'slug' => $slug, | |
'comments' => $comments, | |
]); | |
... lines 38 - 39 | |
} | |
... lines 41 - 52 | |
} |
The method we want to call on the Twig object is coincidentally the same as the controller shortcut.
Then at the bottom, return new Response()
and pass $html
:
... lines 1 - 8 | |
use Symfony\Component\HttpFoundation\Response; | |
use Twig\Environment; | |
class ArticleController extends AbstractController | |
{ | |
... lines 14 - 24 | |
public function show($slug, Environment $twigEnvironment) | |
{ | |
... lines 27 - 32 | |
$html = $twigEnvironment->render('article/show.html.twig', [ | |
'title' => ucwords(str_replace('-', ' ', $slug)), | |
'slug' => $slug, | |
'comments' => $comments, | |
]); | |
return new Response($html); | |
} | |
... lines 41 - 52 | |
} |
Ok, this is way more work than before... and I would not do this in a real project. But, I wanted to prove a point: when you use the $this->render()
shortcut method on the controller, all it really does is call render()
on the Twig service and then wrap it inside a Response
object for you.
Try it! Go back and refresh the page. It works exactly like before! Of course we will use shortcut methods, because they make our life way more awesome. I'll change my code back to look like it did before. But the point is this: everything is done by a service. If you learn to master services, you can do anything from anywhere in Symfony.
There's a lot more to say about the topic of services, and so many other parts of Symfony: configuration, Doctrine & the database, forms, Security and APIs, to just name a few. The Space Bar is far from being the galactic information source that we know it will be!
But, congrats! You just spent an hour getting an awesome foundation in Symfony. You will not regret your hard work: you're on your way to building great things and, as always, becoming a better and better developer.
Alright guys, seeya next time!
Hey Marine GREGOIRE!
Thank you for your kind words - we're super happy any time they're useful ❤️❤️
unfortunately haven't seen anything about dynamic subdomains in S4
You're right! It's a bit of an edge-case feature. Well, I mean, it's not that rare - but we sometimes don't have time to get to more specific situations. However, a LONG time ago, we did have a small topic about this: https://symfonycasts.com/screencast/question-answer-day/symfony2-dynamic-subdomains
That is for Symfony 2, so you'll need to "translate" for Symfony 4. And, your project's requirements may make this vary a little... or a lot. But, the tl;dr is this (I'm assuming some things about your project):
1) Create an entity (e.g. Client, Site, etc) that has a subdomain
property
2) Create a listener on the kernel.request event (or, the new event name RequestEvent
), look at the current URL and find the correct Client
based on that URL.
3) Set the "active" client on some SiteManager
service, so that you can fetch that anywhere you need it and say something like $siteManager->getActiveClient()
.
Let me know if this helps - or if my assumptions about your app were way off ;).
Cheers!
You're absolutely right. I'm going to go deeper in the one made in Symfony 2 then and follow your advice.
Because I'm in dev environment for now, I was wondering if I should use the Symfony binary to have a local webserver where I can test the subdomains? (Because I can't see how I'm supposed to do that with my localhost:8000 ;)
Hey Marine GREGOIRE!
GREAT question! The Symfony binary absolutely supports doing this - you can find the details here: https://symfony.com/doc/cur...
Cheers!
Hi Ryan !
So I've managed to do all that and it works perfectly ! I even made my service available as a global for twig to get my client available on base.html.twig and everywhere on the website.
Thank you so much for your help and what you've done with the team with these casts. ;)
Thanks Guys for this great tutorial. It was awesome and for me it is a new beginning . Peace !!!!!!!!
Best!
We're glad to hear it! Welcome to the Symfony world if you have questions about any tutorial just let us know :)
Cheers!
Awesome Dear Victor You know What I learn From You not Symfony only the biggest thing I learn from you Fun Feeeeeeeeeeeeeeeesssssssssssssssss!!!! When I was learning some coding then my mood was serious ed in that time when the error fall I frustrated and dejected from the code then I was thing is so hard. But In symfony every errror was Fun. You are of the one Instructor in the World That my Voice I would be proud of you. It's traaaaaaaaaaaaaaaap!
Thank's Sir I have no word to pay you thanks. I am obliged by you!!!
Haha, hey John99Dev! You have made our day! I've shared your message with our entire group (including Victor)! Now, keep going!! :D
Thank's Alooooooooooooooooooooooooooooooooooooooooooooooooooooot. and You made our Life Thank's
I am definitely learning a lot, but I am still struggling a bit understanding some concepts. Is there any advice or if you guys have a roadmap on what I need to learn first before I tackle symfony? Thank you and great work
Hey Travel,
First of all, thank you for the nice feedback about SymfonyCasts!
Hm, it depends on what concepts exactly. Well, of course, Symfony is written in PHP, so I'd definitely recommend you to know more about PHP programming language. We have a track for this: https://symfonycasts.com/tr... . Also, Symfony is written using object-oriented programming (OOP) way and operates objects. Almost everything in Symfony a separate classes and its instances. So, to understand this OOP concept I'd recommend you to pass our OO track: https://symfonycasts.com/tr... . And after this you should have enough knowledge to start with Symfony. I'd recommend you to follow the latest Symfony 5 version, we have a separate track for this here: https://symfonycasts.com/tr... - we're working on releasing new tutorials right now.
I hope this helps! If you still have any questions - just let us know! And you may also ask more specific questions about the topic you didn't get in comments below the video.
Cheers!
Hi,
When you pass $twigEnvironment as a function parameter in the minute 5:30, how does Symfony knows what to load from it? It isn't like the usual parameter you pass to a function, for example a string or an integer. I search a bit in Google and it seems related with Dependency Injection, but I don't understand how it works in this case. Maybe this is explained in a later video and I'm getting ahead, but I would appreciate if you can point me to any resource explaining this.
Thanks.
Hey @dan!
This is all powered by "autowiring", which works by the type-hint of the argument (in this case Environment
). Earlier in this video, we run bin/console debug:autowiring
- https://symfonycasts.com/screencast/symfony/services?playAt=250 - this gives us a list of all of the type-hints that we can use to "ask" the container for specific services. The Twig\Environment
service is on that list.
So, you're 100% correct to ask "how does Symfony know what to load for the $twigEnvironment variable?". The answer is that the container (sometimes called the "dependency injection container") holds a lot of services (objects). When you want to "ask" for one of those services in a controller, you add an argument and use whatever type-hint gives you that service. When Symfony goes to call the controller, it sees the type-hint and asks the container to give it the correct service so it can pass it as the argument.
By the way, you will also see this autowiring thing used in the __construct()
method of your own services - that's actually the main/most traditional place that you'll see it.
Let me know if that helps!
Cheers!
Thank you very much for this great tutorial ❤️❤️ , I have a problem with the security-checker, someone has a track?
λ php bin\console security:check
In ErrorChunk.php line 105:
SSL peer certificate or SSH remote key was not OK for "https://security.symfony.co...".
In CurlResponse.php line 264:
SSL peer certificate or SSH remote key was not OK for "https://security.symfony.co...".
Hey Hassan G.
Is this problem still persist? It looks like temporary certificate issue, and it should work fine. Let us know if you still have this error.
Cheers.
Hello @Vsadikoff, thank you for your return, I still have the error, I turned an composer update and the error persist
Hey Hassan G.!
Yes, our sensiolabs/security-checker
package in the tutorial is out of date. If you want to fix it, you'll need to update the sensiolabs/security-checker
entry in composer.json to be at least ^5.0
(or better, ^6.0
), which is the newest :). Then run composer update sensiolabs/security-checker
.
Let me know if this helps!
Cheers!
I have a question which is referring to Twig and Services.
Twig has a global app object which comes from AppVariable class.
What I would like to extend this class and use mine class for an app object.
How I can archive that?
I would like to add few more helper methods, in example in controller I want to access app object and add breadcrumbs:$app->setBreadcrumbs($breadcrumbs)
and in template I will access it and render.
Hey Krzysztof K.
I'm glad to know that you could do it by yourself but I have one question for you. Instead of extending the global app object why just don't create a TwigExtension service where you can add your own functions and filters?
Cheers!
ok, I have managed to do that myself.
`
namespace App\Service;
use Symfony\Bridge\Twig\AppVariable;
use Symfony\Component\DependencyInjection\ContainerInterface;
class App extends AppVariable
{
/**
* @var ContainerInterface
**/
protected $container = null;
protected $breadcrumbs = [];
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
/**
* @return array
*/
public function getBreadcrumbs(): array
{
return $this->breadcrumbs;
}
/**
* @param array $breadcrumbs
*/
public function setBreadcrumbs(array $breadcrumbs): void
{
$this->breadcrumbs = $breadcrumbs;
}
/**
* @param array $breadcrumb
*/
public function addBreadcrumb(array $breadcrumb): void
{
$this->breadcrumbs = array_merge($this->breadcrumbs, $breadcrumb);
}
}
App\Service\App:
arguments: ['@service_container']
calls:
- [setEnvironment, ["%kernel.environment%"]]
- [setDebug, ["%kernel.debug%"]]
- [setTokenStorage, ['@?security.token_storage']]
- [setRequestStack, ['@?request_stack']]
twig:
globals:
app: '@App\Service\App'
`
Hello,
in the symfony 4.2.0 version there is probably a bug.
When you run:
./bin/console debug:autowiring
list of services is empty ...
See https://github.com/symfony/...
In my case i have to install 'reflection-docblock' from phpdocumentator
composer require phpdocumentor/reflection-docblock
This solved the problem
Hey Sebastian S.
You are completely right! There was a bug, but it's already fixed in version 4.2.1. Update you symfony version and you will see all services.
By the way thank you for your report and solution to bypass this bug.
Cheers!
Dear victor i have a problem with tail -f var/log/dev.log I get tail: cannot open ‘tail -f var/log/dev.log’ for reading: No such file of directory. Tail: no files remaining.
Please help me.
Hey Frank K.
Could you run ls var/log
so we can see what files you have on that folder?
Side note: Double check that you are running on the "dev" environment, just open ".env" file and look for "APP_ENV" variable
Cheers!
Hey, can you help me an advice how to resolve this issue:
I created a new class Tools in Service folder. In this class Tools I am trying to get the base path to the directory through
$basePath = $this->getParameter('kernel.project_dir');
But I have an error
"Call to a member function getParameter() on null"
I tried class Tools extends Controller but the error remained
https://yadi.sk/i/Ts6Mhe5m3...
If I run $basePath = $this->getParameter('kernel.project_dir') in ExampleController that in folder Controller it is NO error!!
Hey Aleksandr T.!
I can totally help! So, the getParameter()
shortcut method is ONLY something that works when you're inside a controller. And now, you can't just extend Controller - that's a very clever idea, but it won't work. Basically, for convenience, there are a few things that controllers can do that other classes cannot do. And... that's no problem!
So, how can you get a parameter from inside a service? Well, normally, when you have a service (like your Tools class) that needs another service, we create a __construct
method and use autowiring by type-hinting that argument. We start talking about that here, but we talk about it much more in the next tutorial - https://knpuniversity.com/screencast/symfony-fundamentals. However, you cannot autowire parameters, because they are not objects, so there is no type-hint to use. But, the process is almost the same: you should create a __construct() method with a $projectDir
argument. Then, to tell Symfony what to pass for this, you'll use the bind keyword in services.yaml (https://knpuniversity.com/screencast/symfony-fundamentals/controller-constructor) - something like this:
services:
_defaults:
# ...
bind:
$projectDir: '%kernel.project_dir%'
Now, any constructor argument for any service that is called $projectDir
will receive this value.
Cheers!
many thanks Ryan!
I trying but don`t understand how make right binding the service parameter $projectDir
I have an InvalidArgumentException
Unused binding "$projectDir" in service "App\Service\Tools".
I find you contribute
https://symfony.com/blog/new-in-symfony-4-1-autowiring-improvements
but my code still wrong
in config\services.yaml I add
services:
_defaults:
# ...
bind:
$projectDir: '%kernel.project_dir%'```
in my service class src\Service\Tools.php
class Tools
{
private $projectDir;
public function __construct()
{
$this->projectDir = $projectDir;
}
public function test()
{
return $this->projectDir;
}
}`
but when i try to call
class Example extends Controller
{
/**
* @Route("/example", name="example")
*/
public function test(Tools $tools)
{
dump($videoTools->test()); die;
}
Hey Aleksandr T.
Ahh! You're SO close! You're just missing one detail. It's this in Tools
public function __construct($projectDir)
{
$this->projectDir = $projectDir;
}
You forgot to add the $projectDir
argument to the __construct() method. The "bind" feature works by looking at the name of your constructor arguments and "binding" them to the value you specified. Because you didn't have this argument, the error was basically saying:
Hey! I see you configured a "bind" for
$projectDir
in your services.yaml file, but I don't see any$projectDir
arguments in your app.
Let me know if this fixes it!
Cheers!
Whats the difference between:
./bin/console debug:autowiring
and
./bin/console debug:container --types
I had the other command from a SF3 Tutorial of you. It looks more clean in the terminal.
Hey Mike P.!
GREAT question :). And sorry for my slow reply - I was away last week. Here's the PR where I added debug:autowiring: https://github.com/symfony/...
Basically, in Symfony 3.4, the autowiring types become much more important. And, the debug:container --types is a long name, and not easy to discover (you wouldn't easily be able to figure out that this command has a --types option). I also wanted something with a bit less information - hopefully something that looks MORE clean, not less clean ;).
But, this was also a "rushed" feature - I introduced this after the Symfony 3.4 feature freeze, and so it needed to be done quickly and not touch any other parts of the system. We're actively talking about how to not have these 2 separate commands anymore, and, instead, to make one command that really shows the BEST things possible. Here is some info: https://github.com/symfony/...
Overall, they how the same info, just displayed differently.
Cheers!
The new debug:autowiring over at Github looks so cool! Hopefully we'll see it soon! Thanks, you're awesome!
Is it also considered good practice(or does it make any sense?) if you constructor inject 'EngineInterface' and declare 'templating engines' in the 'framework.yml' file?
Hey Sarah,
Well, Symfony suggests using Twig as a template engine, and the question is: why don't you enough Twig that is secure and powerful? But if you need a few engines like Twig and PHP - I think that's OK to use both, it depends on your business logic.
Cheers!
Is there going to be any addition to this course. This is my first ever introduction to Symfony framework and I have already started to like it.
Hey Junaid,
Yes! See Symfony 4 track for more courses: https://knpuniversity.com/t... - there're only . courses yet, there will be more soon.
You can also check Symfony 3 track: https://knpuniversity.com/t... - there much more topics covered, but you will need to do it in Symfony 4 way using Flex like we show here.
Cheers!
I'm getting this error when I run `tail -f /var/log/dev.log`:
tail: /var/log/dev.log: No such file or directory
There is indeed no such file in /var/log. Is this a permissions problem maybe? Here's what I have:
/var/log
drwxr-xr-x
owner: root
group: wheel
I haven't changed any of the default configuration that was installed with monolog. Here is my config/packages/dev/monolog.yaml:
```yaml
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
# type: firephp
# level: info
#chromephp:
# type: chromephp
# level: info
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
```
Hey agentolivia ,
Easy fix ;)
Just remove slash from the beginning of the path, i.e.
tail -f var/log/dev.log
When you start with "/" - you look for the file relative to the root of your file system, but we need to look for it relative to the Symfony project directory. And as you can see, we don't have leading slash in paths we have in this screencast.
Cheers!
Hey Sandeep,
This file is created when you load application in "dev" environment. But this path is different for Symfony 3 applications where logs are written in "app/logs/dev.log" or "var/logs/dev.log". So you need to figure out the path to logs for your *project*. But really, if you don't see logs file when load Symfony application and you even can't load the application at all, i.e. you see some server error (not Symfony error page) - then probably you need to check logs of your web server like Nginx / Apache.
Cheers!
Actually, i think it is good practice to inject the Environment service into your action and to not use the render shortcut.
This way you can mock the twig rendering for unit testing.
Hey Hansi,
If you want to unit-testing it - so yeah, it makes sense to inject Twig service. And that's totally fine I think. But mostly controllers are tested with functional tests, so it's ok to use shortcuts.
Cheers!
Do all services get instantiated when symfony starts? Even services that you do not need. Thxs in advance.
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"sensio/framework-extra-bundle": "^5.1", // v5.1.3
"symfony/asset": "^4.0", // v4.0.3
"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.3
"symfony/web-server-bundle": "^4.0", // v4.0.3
"symfony/yaml": "^4.0" // v4.0.14
},
"require-dev": {
"easycorp/easy-log-handler": "^1.0.2", // v1.0.4
"sensiolabs/security-checker": "^5.0", // v5.0.3
"symfony/debug-bundle": "^3.3|^4.0", // v4.0.3
"symfony/dotenv": "^4.0", // v4.0.14
"symfony/monolog-bundle": "^3.0", // v3.1.2
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.3
"symfony/profiler-pack": "^1.0", // v1.0.3
"symfony/var-dumper": "^3.3|^4.0" // v4.0.3
}
}
First of all I want to tell you guys I love your tutorials, they're so well done and help A LOT !
I went through all of them but unfortunately haven't seen anything about dynamic subdomains in S4. Is there anything plan for these soon ?
Thanx a lot for all you've done ;)