Intro to Services

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:

24 lines src/AppBundle/Controller/GenusController.php
... lines 1 - 5
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
... lines 7 - 8
class GenusController extends Controller
{
... lines 11 - 22
}

To get the templating service, add $templating = $this->container->get('templating'):

24 lines src/AppBundle/Controller/GenusController.php
... lines 1 - 8
class GenusController extends Controller
{
... lines 11 - 13
public function showAction($genusName)
{
$templating = $this->container->get('templating');
... 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:

24 lines src/AppBundle/Controller/GenusController.php
... lines 1 - 8
class GenusController extends Controller
{
... lines 11 - 13
public function showAction($genusName)
{
$templating = $this->container->get('templating');
$html = $templating->render('genus/show.html.twig', array(
'name' => $genusName
));
... 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:

24 lines src/AppBundle/Controller/GenusController.php
... lines 1 - 8
class GenusController extends Controller
{
... lines 11 - 13
public function showAction($genusName)
{
... 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:

2 lines app/Resources/views/genus/show.html.twig
<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.

Leave a comment!

  • 2016-06-20 Victor Bocharsky

    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!

  • 2016-06-18 Jelle Schouwstra

    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

  • 2016-06-18 weaverryan

    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!

  • 2016-06-18 Jelle Schouwstra

    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?

  • 2016-05-18 Konrad Zając

    Ok, thanks !

  • 2016-05-18 weaverryan

    Hi Konrad!

    Good job debugging :). The problem is that auto-complete will suggest *both* classes. For PhpStorm, it sees 2 classes that are named Controller. It doesn't know which one is the correct class and which one is the incorrect class - it simply suggests to you both classes that are called "Controller". The one in HttpKernel\Tests is just an internal helper class for helping core Symfony run its own tests. But, PhpStorm doesn't know this.

    Usually, when you use auto-complete, there is only 1 matching class name, but sometimes there are multiple, so you need to be careful to choose the correct one. Another example is Symfony's Response class. There are two: one in the HttpFoundation component (this is the one you want) and one in the BrowserKit component (not the one you want).

    I hope this clarifies!

  • 2016-05-14 Konrad Zając

    Hi,
    Firstly great tuturial ;)
    Secondly I did all the steps but I get
    "Attempted to load class "Controller" from namespace "Symfony\Component\HttpKernel\Tests".
    Did you forget a "use" statement for "Symfony\Bundle\FrameworkBundle\Controller\Controller"?"

    my use statement after autocompletion is use Symfony\Component\HttpKernel\Tests\Controller;
    and in this tutorial it's use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    what's the correct way?

    ---------Edit-------------

    OK, pasted the second one and it works - but still , why does my autcompletion gives me the wrong one?

  • 2016-05-04 Jay

    Oh my, sorry I didn't notice that :) it's now working thanks for the help!

  • 2016-05-03 weaverryan

    Hi Jay!

    You're getting some really good errors - because these are some of the most common errors. So once you learn how to read them, you'll be dangerous :).

    For this error, Symfony is literally saying: "I looked through all of your routes, but didn't find any that match the URL /". Remember, you can get a list of *all* of the routes you've built by running:


    bin/console debug:router

    In this case, we have not (at this point) created an route for the URL "/". Try changing the URL in your browser to http://localhost:8000/genus/octopus.

    You may have other errors when you get to that page, but this is definitely the cause of *this* error. About the auto-completing, do you have the Symfony plugin installed *and* enabled for this project?

    Cheers!

  • 2016-05-03 Jay

    Thanks for the reply! the path is correct and now i'm getting this error:
    No route found for "GET /"
    404 Not Found - NotFoundHttpException
    1 linked Exception:
    ResourceNotFoundException ».

    The cache is already mark as excluded but the auto-complete in get('templating') doesn't appear even when i'm pressing ctrl + spacebar and it says "No suggestions".
    i still get the error even if i change the code into this:
    return $this->render('genus/show.html.twig', [
    'name' => $genusName
    ]);

  • 2016-05-02 weaverryan

    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!

  • 2016-05-02 Jay

    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 »