Chapters
Intro to Services
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Ok! The first half of Symfony: route-controller-response is in the books!
The second half is all about useful objects. Obviously, returning a string response like this is not going to take us very far. Our aquanauts demand more! In real life, we might need to render a template, query a database, or even turn objects into JSON for an API!
Symfony comes with many, optional, useful objects to help out with stuff like this. For example, want to send an email? Symfony has an object for that. How about log something? There's an object for that.
These objects are commonly called services, and that's important: when you hear the word service
, just think "useful object".
Service Container
To keep track of all of these services, Symfony puts them into one big associative array called the container. Each object has a key - like mailer
or logger
. And to be more honest with you - sorry, I do like to lie temporarily - the container is actually an object. But think of it like an array: each useful object has an associated key. If I give you the container, you can ask for the logger
service and it'll give you that object.
The second half of Symfony is all about finding out what objects are available and how to use them. Heck, we'll even add our own service objects to the container before too long. That's when things get really cool.
Accessing the Container
The first useful object is the templating
service: it renders Twig templates. To get access to the service container, you need to extend Symfony's base controller.
Go Deeper!
Why does extending Controller
give you access to the container? Find out:
Injecting the Container: ContainerAwareInterface (advanced).
In GenusController
, add extends Controller
from FrameworkBundle
. Hit tab to autocomplete and get the use
statement:
Show Lines
|
// ... lines 1 - 5 |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
Show Lines
|
// ... lines 7 - 8 |
class GenusController extends Controller | |
{ | |
Show Lines
|
// ... lines 11 - 22 |
} |
To get the templating
service, add $templating = $this->container->get('templating')
:
Show Lines
|
// ... lines 1 - 8 |
class GenusController extends Controller | |
{ | |
Show Lines
|
// ... lines 11 - 13 |
public function showAction($genusName) | |
{ | |
$templating = $this->container->get('templating'); | |
Show Lines
|
// ... lines 17 - 21 |
} | |
} |
The container pretty much only has one method: get
. Give it the nickname to the service and it will return that object. It's super simple.
Quickly, open the var
directory, right click on the cache
directory and click "mark this directory as excluded". Symfony caches things... that's not important yet, but excluding this is important: this directory confuses autocompletion.
Now type $this->container->get('templating')
. Well hey autocompletion!
Rendering a Template
With the templating object we can... well... render a template! Add $html = $templating->render('')
followed by the name of the template. This could be anything, but let's be logical: genus/show.html.twig
. I'll show you where this lives in a second:
Show Lines
|
// ... lines 1 - 8 |
class GenusController extends Controller | |
{ | |
Show Lines
|
// ... lines 11 - 13 |
public function showAction($genusName) | |
{ | |
$templating = $this->container->get('templating'); | |
$html = $templating->render('genus/show.html.twig', array( | |
'name' => $genusName | |
)); | |
Show Lines
|
// ... lines 20 - 21 |
} | |
} |
We'll also want to pass some variables into the template. Pass a name
variable into Twig that's set to $genusName
.
Finally, what do we always do in Symfony controllers? We always return a Symfony's Response
object. Stick, that $html
into the response object and return it:
Show Lines
|
// ... lines 1 - 8 |
class GenusController extends Controller | |
{ | |
Show Lines
|
// ... lines 11 - 13 |
public function showAction($genusName) | |
{ | |
Show Lines
|
// ... lines 16 - 20 |
return new Response($html); | |
} | |
} |
Go Deeper!
You can actually return anything from a controller via the kernel.view
event:
The kernel.view Event (advanced)
Create the Template
Ok, where do templates live? Ah, it's so simple: templates live in app/Resources/views
. The one we're looking for will be in app/Resources/views/genus/show.html.twig
. The existing index.html.twig
template was for the original homepage. Check it out if you want to, then delete it!
Create a new genus
directory and then a new file: show.html.twig
. Welcome to Twig! You'll love it. Add an <h1>
tag with The Genus
and then {{ name }}
to print the name
variable. More on Twig in a second:
<h1>The Genus {{ name }}</h1> |
But that's it! Refresh the browser. Check out that sweet h1
tag.
Now back up: we just did something really cool: used our first service. We now know that rendering a template isn't done by some deep, dark part of Symfony: it's done by the templating object. In fact, Symfony doesn't really do anything: everything is done by one of these services.
56 Comments
Gracias !!!
Hey Kaira S!
Bah.... sorry about that - didn't mean to mislead :D. But, I hope your perfectionist at least made you learn some great stuff while digging!
Cheers!
lol...thats ok....used to beating my head against something every time I start something new.
Did learn a couple things here and there.
1. You can't open a symfony project on xampp without first declaring a virtual host for it since the web root is web(xampp requires vhost setup for it like so)
<virtualhost genus="">
DocumentRoot "G:/xampp/htdocs/genus/web/"
ServerName genus
</virtualhost>
then a host domain of 127.0.0.1 genus
(that is what works for me..since I have multiple projects going.)
2. Clearing the cache is really important when making changes, else xampp will not see the changes most of the time.
Just a few tidbits I thought might help someone else....
Great course by the way...Love it!
Hey Kaira S
Thanks for sharing your findings! I bet more than one will find them helpful :)
About *2*: While in dev environment you should not need to clear cache that often, I'm not sure if there is something special with XAMPP because I'm on ubuntu, but *maybe* you have something odd in your configuration
Have a nice day
Running xampp with php 7.2 straight out of the box and has worked for wordpress installs(non-bitnami app installs).
It only seems to have an effect when I change a controller and not any of the twigs.
Have a Great Day
Hmm, interesting, makes me wonder if you have some kind of cache activated, or if you apache is caching some files by default
Think I figured out the cache problem....haven't had it since i changed the vhost declaration...document path was project/web/ and after i added app_dev.php to it everything works great (so far...knock on wood) .
without the app_dev.php at the end I believe it was xampp was trying to use the production version app.php and that was causing a conflict in apache.
Yeah! :)
Have A Great Day!
That is! You were hitting production, that's why everything was getting cached. Nice work man :)
Cheers!
Even better solution found!
After I figured out the cache problem....got into the templating section of aqua_notes and found HEY Woah! The style sheets, images and js were not loading when viewing the page using my xampp server.
Figured it out. you have to set up the virtual host directive a little differently than most info on the web will tell you..then Symfony works and plays great with Xammp. Here's an example vhost config for a project called my-project. The form validation here on Disqus might screw up the example but we'll try.
Just as I thought....the double ="' s are not part of the solution, that is the form validation in Disqus stepping in for possible protection for the forward slashes and greater sign symbol, but you get the picture.
<virtualhost my-project:80="">
DocumentRoot "C:/xampp/htdocs/my-project/web"
ServerName my-project
<directory c:="" xampp="" htdocs="" my-project="" web="">
AllowOverride None
Order Allow,Deny
Allow from All
Options FollowSymlinks
<ifmodule mod_rewrite.c="">
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
</ifmodule>
</directory>
ErrorLog "logs/my-project-error.log"
CustomLog "logs/my-project-access.log" common
</virtualhost>
Just make sure the app.dev Kernel directive "prod" is set to false and your good to go!
Hope this helps those windows xammp user struggling to get things up and running.
Have a great day!
Yes! I'm glad to hear that you could fix your problem :)
Thanks for sharing your solution
cheers!
Lots of thanks
reemplace
$templating = $this->container->get('templating');
by
$templating=$this->container->has('templating');
While writing $templating = $this->container->get('templating');(1:55) no autocompletion and also shows missing service warning even after excluding cache folder!! What to do ?
Hey Mohit
Which version of PhpStorm and Symfony are you using? Remember that you need to activate Symfony's plugin first
Some times my index is all wrong and I fix it by running "$ composer install", it may work for you as well.
Cheers!
Hi Diego Aguiar, Thanks for the help , but composer install doesn't fixed anything in my case. I am still getting service not found error.I am using symfony 3.4.4 and PhpStorm 2017.3.4 . In autocompletion I am getting 2 option for templating service , templating.helper.logout_url and templating.helper.security !!
Hey Mohit
As far as I know the "Templating" service was disabled for Symfony 3.4 version, to verify this, you can run: "$ php bin/console debug:container templating" or you can try to actually use it (ignoring the fact that you don't have autocompletion).
If that's the case, well, you should not be worry too much about it, because you can use "$this->render()" function at any controller that inherits from Symfony's base controller
Cheers!
If you run into a problem that Symfony v3.4 tells you about non-existent service "templating":
You have requested a non-existent service "templating". Did you mean one of these:
"templating.helper.logout_url", "templating.helper.security"?
you need to add the following configuration into your app/config/config.yml:
framework:
templating:
engines: ['twig']
(Note: there probably already is "framework" section so just add the "templating: engines: ['twig']" part into it.)
Thanks a lot Miroslav Šustek, this error was making me crazy.
solved... ignore this..
+1 good tip! In 3.4, that config was removed, as the templating component is not being "recommended" anymore. But, there's no problem with activating it. It's just that, as we move forward, less and less bundles will use this component. So, at some point, you won't need it (things are now using Twig directly).
Cheers!
You have requested a non-existent service "templating". Did you mean one of these: "templating.helper.logout_url", "templating.helper.security"?
Hey Mehul,
Just use "twig" directly instead. Also, you can follow this thread to get more info about it: https://symfonycasts.com/sc...
Cheers!
Great tutorial, one problem though, I get "Notice: Undefined variable: templating"
And an exception:
Uncaught PHP Exception Symfony\Component\Debug\Exception\ContextErrorException: "Notice: Undefined variable: templating" at D:\media\sandbox\test\symfony2016\src\AppBundle\Controller\GenusController.php line 23
Context: [exception => Object(Symfony\Component\Debug\Exception\ContextErrorException)]
I've followed your tutorial and this is my controller code:
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;
* @Route("/genus/{genusName}")
* */
public function showAction($genusName){
$templating->$this->container->get('templating');
$html = $templating->render('genus/show.html.twig', [
'name' => $genusName
]);
I don't use phpstorm but Netbeans, perhaps I used a wrong namespace?
Hey Jelle!
Ya know, the hardest things to track down are the smallest mistakes. And this is small! :)
Here's the problem:
// you have
$templating->$this->container->get('templating');
// you should have
$templating = $this->container->get('templating');
That's it :). Hence the undefined variable error - it thinks you're trying to use a $templating variable, not set one! Your editor should highlight this for you with an error. I like PhpStorm better than NetBeans, but that's a solid editor - check to see if there's any red highlighting around this area that would help you spot things like this.
Cheers!
Aah Yes, I see it now! Thanks a bunch! I would love to use PHPstorm if it was free to use, in contrast to Netbeans, that's why my choice has fallen on the last mentioned
Hey, Jelle!
BTW, if you would love to be a PhpStorm beta tester, you could use it for free. Check PhpStorm Early Access Program, each month you will be able to download a new build of PhpStorm beta. It's a new fully featured version of PhpStorm, which will be released in the future. You need to update it to each time when new build is released to renew your trial licence.
Cheers!
Thanks for tutorials.
Anyway.. I didn't use code editor like yours. How to exclude cache then?
Hey Cleo,
Excluding cache depends on your IDE, you need to Google for it to find a solution for *your* IDE. I think you can find something useful on StackOverflow. Anyway, it's not a big deal to exclude the cache dir, so you can skip this step if you can't find how to do it. But PhpStorm is our favorite PHP IDE, and with Symfony plugin it's the best for Symfony, I really advise you to try it. You can check its most useful features in our screencast: Lean and Mean Dev with PhpStorm (for Symfony).
Cheers!
Hows it going, im getting a missing service on 'templating', Ive excluded the cache directory aswell. If i continue with the tutorial , the site operates, but, i still have mising service?
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class BibliographyController extends Controller
{
/**
* @Route("/Bibliography/{wildcard}")
*/
public function showAction($wildcard)
{
$templating = $this->container->get('templating');
$html = $templating->render('Bibliography/show.html.twig',
[
'name'=>$wildcard
]);
return new Response($html);
}
}
Hey Ross!
If you are extending from BaseController (I can see you do) you can use "$this->render()" directly, anyways that error you are experimenting is kind of weird, are you using Symfony Standard Edition ?
If you do, maybe there is a problem with your configuration, I'd like to see your configuration files so I can check if everything is ok.
Have a nice day!
Hows it going Diego, thanks for the reply! What config files? config.yml? i continued with the tutorial and have also implemented the database, but i am still geting the missing service, aslo put services.yml at the end of the post.
namespace AppBundle\Controller;
use AppBundle\Entity\Bibliography;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
//@Route("/Bibliography")
/**
* Class BibliographyController
* @package AppBundle\Controller
*
*/
class BibliographyController extends Controller
{
/**
* @Route("/Bibliography/new")
*/
public function newAction()//order of route...
{//adding to database
$bib = new Bibliography();
$bib->setName('Ross');
$bib->setDif('Test');
$entityManager = $this->getDoctrine()->getManager();//get manager
$entityManager->persist($bib);//tell to save
$entityManager->flush();// commit to the addition
return new Response('<html><body>Added Ross to database</body></html>');
}
/**
* @Route("/Bibliography/{wildcard}")
*/
public function showAction($wildcard)
{
$notes = [
'New entry: Latest technology in 3d accelerated hardware',
'Old entry: A study on the latest in game graphics',
'Admin entry: Update database'
];
return $this->render('Bibliography/show.html.twig', [
'name'=>$wildcard,
'notes'=> $notes
]);
}
}
Also i think the config files are config.yml?
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
# Put parameters here that don't need to change on each machine where the app is deployed
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: en
framework:
#esi: ~
#translator: { fallbacks: ['%locale%'] }
secret: '%secret%'
router:
resource: '%kernel.root_dir%/config/routing.yml'
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
#serializer: { enable_annotations: true }
templating:
engines: ['twig']
default_locale: '%locale%'
trusted_hosts: ~
trusted_proxies: ~
session:
# http://symfony.com/doc/current/reference/configuration/framework.html#handler-id
handler_id: session.handler.native_file
save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
fragments: ~
http_method_override: true
assets: ~
php_errors:
log: true
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: '%database_name%'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/../var/data/data.sqlite"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
#path: '%database_path%'
orm:
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: '%mailer_transport%'
host: '%mailer_host%'
username: '%mailer_user%'
password: '%mailer_password%'
spool: { type: memory }
Services...................
# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/service_container.html
parameters:
#parameter_name: value
services:
#service_name:
# class: AppBundle\Directory\ClassName
# arguments: ['@another_service_name', 'plain_value', '%parameter_name%']
I have aslo noticed, in my src folder, i dont have AppBundle\Directory\ClassName? just AppBundle\Controller, if that helps.
Hey Ross! I hope you had a great weekend :)
Yeah the config files can be .yml or .xml, in your case are .yml
Your configuration looks good to me, I'm not sure what is causing it to don't find the service
Can you double check that you are not running in production mode ?
In a controller method just do this, so you can be 100% sure:
$env = $this->get('kernel')->getEnvironment();
dump($env); die;
Also run again composer install and clear the cache, just in case
"I have aslo noticed, in my src folder, i dont have AppBundle\Directory\ClassName?"
You don't have it because is just an example of how to point to a service class, by writting the full namespace of that class
Cheers!
$templating = $this->container->get('templating'); phpstrom shows "missing service"
I cleared the cache and cache folder IS excluded. I don't know how to fix this issue and I'd be glad if you can help me.
It works fine in version 3.0 but not in 3.3 and I have no idea why!
Hey Ali,
Is it only autocompletion problems, I mean this code works well, you just don't have autocompletion in PhpStorm? Does this problem only for "templating" service or you have the same for other services? Do you have the latest version of Symfony Plugin and PhpStorm?
Cheers!
Hey,
Yes. It's just the autocompletion that is not working and the code actually works. Also I checked another service, for example "mailer" and autocompletion worked:
$this->container->get('mailer');
However as I said, phpstrom is not suggesting me 'templating'.
Hey Ali Niaki!
Actually, I see the same behavior in Symfony 3.3! And it looks like a bug in the PhpStorm plugin - I've opened an issue: https://github.com/Haehnche.... For now... ignore it! Hopefully it's an easy fix in the plugin :).
Cheers!
thX! I would look forward to undrestand how to fix it :)
Looks like it just got fixed - we just need to wait for a new version of the plugin to be released! Woohoo!
I suppose the issue has finished just to say that if i put in the container->get('templating.engine.twig') and not the alias 'templating' it works fine for me!
Hey Ali,
Good catch! Until it will be fixed, you can use annotation above the variable to get autocompletion temporarily:
/** @var \Symfony\Bundle\FrameworkBundle\Templating\EngineInterface $templating */
$templating = $this->container->get('templating');
Cheers!
Thank you for helping but it didn't work. I guess I'm just gonna ignore it for now as victor said since I'm going to use the sortcut for it:
$this->render();
It should work, because it is not Symfony Plugin feature but Core PhpStorm one :) Make sure you store the reference to the templating service in variable first. Or maybe I was wrong with interface, try to reference service class directly, it should be "\Symfony\Bundle\TwigBundle\TwigEngine" as I know:
/** @var \Symfony\Bundle\TwigBundle\TwigEngine $templating */
$templating = $this->container->get('templating');
// And you will get autocompletion here
$templating->render();
Cheers!
Same in ControllerTrait.php
phpstrom is not finding 'templating' in that file too, for example in render function line 231:
protected function render($view, array $parameters = array(), Response $response = null)
...
return $this->container->get('templating')->renderResponse($view, $parameters, $response);
...
get('templating'): missing service
Thank you for tutorials!
Here's my problem:
“
Unable to find template "genus/show.html.twig" (looked into: D:\xampp\htdocs\novi_symfony_projekt\app/Resources/views, D:\xampp\htdocs\novi_symfony_projekt\vendor\symfony\symfony\src\Symfony\Bridge\Twig/Resources/views/Form).
500 Internal Server Error - InvalidArgumentException
1 linked Exception: Twig_Error_Loader »
Here's the code:
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class GenusController extends Controller {
/**
* @route("/genus/{genusName}")
*/
public function showAction($genusName)
{
$templating = $this->container->get('templating');
$html = $templating->render('genus/show.html.twig', [
'name' => $genusName
]);
return new Response($html);
}
}
Hey Tomislav!
Can you double check that your template lives in "app/Resources/views/genus/show.html.twig"
if so, then you might be running in production mode, while in production, you have to clear cache every time you make a modification, but in this case you want to run in development mode, so you can forget about those pesky problems
Have a nice day!
I'll check it, thank you very much!
why am i getting this error "Typo: In word 'templating'
Spellchecker inspection helps locate typos and misspelling in your code, comments and literals, and fix them in one click."
i get that in this code:
$templating = $this->container->get('templating');
i followed the instruction but the auto complete in templating didnt show.
and this is the error when i load the page:
Unable to find template "genus/show.html.twig" (looked into: C:\wamp\bin\aqua_note\app/Resources/views, C:\wamp\bin\aqua_note\vendor\symfony\symfony\src\Symfony\Bridge\Twig/Resources/views/Form).
500 Internal Server Error - InvalidArgumentException
1 linked Exception:
Twig_Error_Loader »
Hi Jay!
You should ignore the "Typo" - that's PhpStorm being *too* helpful. It literally is saying "The word templating isn't a real English word, maybe it's a typo?". It *is* the correct "service id" to use :).
About your error, you can see that the templating service is *trying* render your template, but it can't find it. This literally means that it's looking for it at app/Resources/views/genus/show.html.twig, but it doesn't see a file there. Double-check that you have that file (and that it's in the right location) - that's probably the error. But let me know either way
Cheers!
"Houston: no signs of life"
Start the conversation!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"composer/package-versions-deprecated": "^1.11" // 1.11.99
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0" // v3.1.3
}
}
Finally after 5 days of beating my head against the scattered documentation wall, i found the solution (for my setup anyway) maybe it will help others.
Update: Okay(DuhMe)...if i'd only watched the video in the next chapter Ryan shows this fix but, I wanted to get things working properly before advancing to the next chapter...(The perfectionist in me i guess....:) )
Found @ https://symfony.com/doc/2.8...
I'm running SF 3.4 and this solution works for it!
replace:
$templating = $this->container->get('templating');
$html = $templating->render('genus/show.html.twig',
[
'name' => $genusName
]);
return new Response($html);
with:
return $this->render('genus/show.html.twig', [ 'name' => $genusName ]);
I hope this helps someone else,
Happy Coding