Buy

Entities, Twig and the Magic dot Syntax

Let's finally make this page real with a template. Return $this->render('genus/list.html.twig') and pass it a genuses variable:

91 lines src/AppBundle/Controller/GenusController.php
... lines 1 - 11
class GenusController extends Controller
{
... lines 14 - 33
public function listAction()
{
$em = $this->getDoctrine()->getManager();
$genuses = $em->getRepository('AppBundle:Genus')
->findAll();
return $this->render('genus/list.html.twig', [
'genuses' => $genuses
]);
}
... lines 45 - 89
}

You know what to do from here: in app/Resources/views/genus, create the new list.html.twig template. Don't forget to extend base.html.twig and then override the body block. I'll paste a table below to get us started:

18 lines app/Resources/views/genus/list.html.twig
{% extends 'base.html.twig' %}
{% block body %}
<table class="table table-striped">
<thead>
<tr>
<th>Genus</th>
<th># of species</th>
</tr>
</thead>
<tbody>
... lines 12 - 14
</tbody>
</table>
{% endblock %}

Since genuses is an array, loop over it with {% for genus in genuses %} and add the {% endfor %}. Next, just dump out genus inside:

18 lines app/Resources/views/genus/list.html.twig
... lines 1 - 10
<tbody>
{% for genus in genuses %}
{{ dump(genus) }}
{% endfor %}
</tbody>
... lines 16 - 18

Looks like a good start - try it out!. Ok cool - this dumps out 4 Genus objects. Open up the tr. Bring this to life with a td that prints {{ genus.name }} and another that prints {{ genus.speciesCount }}. And hey, we're getting autocompletion, that's kind of nice:

21 lines app/Resources/views/genus/list.html.twig
... lines 1 - 10
<tbody>
{% for genus in genuses %}
<tr>
<td>{{ genus.name }}</td>
<td>{{ genus.speciesCount }}</td>
</tr>
{% endfor %}
</tbody>
... lines 19 - 21

The Magic Twig "." Notation

Refresh! Easy - it looks exactly how we want it. But wait a second... something cool just happened in the background. We printed genus.name... but name is a private property - so we should not be able to access it directly. How is this working?

81 lines src/AppBundle/Entity/Genus.php
... lines 1 - 10
class Genus
{
... lines 13 - 22
private $name;
... lines 24 - 39
public function getName()
{
return $this->name;
}
... lines 44 - 79
}

This is Twig to the rescue! Behind the scenes, Twig noticed that name was private and called getName() instead. And it does the same thing with genus.speciesCount:

81 lines src/AppBundle/Entity/Genus.php
... lines 1 - 10
class Genus
{
... lines 13 - 32
private $speciesCount;
... lines 34 - 59
public function getSpeciesCount()
{
return $this->speciesCount;
}
... lines 64 - 79
}

Twig is smart enough to figure out how to access the data - and this lets us keep the template simple.

With that in mind, I have a challenge! Add a third column to the table called "Last Updated":

23 lines app/Resources/views/genus/list.html.twig
... lines 1 - 4
<thead>
<tr>
<th>Genus</th>
<th># of species</th>
<th>Last updated</th>
</tr>
</thead>
... lines 12 - 23

This won't work yet, but what I I want to be able to say is {{ genus.updatedAt }}. If this existed and returned a DateTime object, we could pipe it through the built-in Twig date filter to format it:

23 lines app/Resources/views/genus/list.html.twig
... lines 1 - 11
<tbody>
{% for genus in genuses %}
<tr>
<td>{{ genus.name }}</td>
<td>{{ genus.speciesCount }}</td>
<td>{{ genus.updatedAt|date('Y-m-d') }}</td>
</tr>
{% endfor %}
</tbody>
... lines 21 - 23

But this won't work - there is not an updatedAt property. We'll add one later, but we're stuck right now.

Wait! We can fake it! Add a public function getUpdatedAt() and return a random DateTime object:

85 lines src/AppBundle/Entity/Genus.php
... lines 1 - 10
class Genus
{
... lines 13 - 79
public function getUpdatedAt()
{
return new \DateTime('-'.rand(0, 100).' days');
}
}

Try that out. It works! Twig doesn't care that there is no updatedAt property - it happily calls the getter function. Twig, you're awesome.

Leave a comment!

  • 2017-05-08 Victor Bocharsky

    Hey maxii123 ,

    I'm not sure Symfony Plugin reads the annotation in twig templates. Do you use the latest PhpStorm version? Check that Symfony Plugin is enabled and has necessary settings. Also, in your controller, you need to have PhpStorm autocompletion for that entity and you should pass this entity variable explicitly in render() function like:


    $this->render('path-to-your-template.html.twig', [
    'entity' => $entity, // Ensure you have autocompletion for $entity in this spot.
    ]);

    And the last but not least advise: try to restart PhpStorm, it helps in most cases ;)

    Cheers!

  • 2017-05-06 maxii123

    Even trying the annotation below

    {# @controller AppBundle:Genus:list #}

    I am not getting twig entity completion in my own project. Suggestions?

  • 2017-05-04 Nobuyuki Fujioka

    Ok, my bad. Thank you.

  • 2017-05-04 Victor Bocharsky

    Hey Fujioka,

    I see what the problem is :) You need to use concatenation instead of comma - all this should be as a first argument for DateTime constructor. So the correct example is: return new \DateTime('-'.rand(0,100).' days');

    Cheers!

  • 2017-05-04 Nobuyuki Fujioka

    Hi, when I followed the tutorial, I get the following error.
    Type error: DateTime::__construct() expects parameter 2 to be DateTimeZone, string given

    My symfony does not seem to like
    public function getUpdatedAt()
    {
    return new \DateTime('-'.rand(0,100),'days');
    }

    Any idea? Why this does not work on mine?

  • 2016-04-19 weaverryan

    Hey Hans!

    Ah, that's very interesting - you're hinting your template - very cool! In my experience, the variables work sometimes, but not all of the time - I'm not sure exactly what logic PhpStorm is using to "guess" the controller for the template. I assume it's based on naming conventions (GenusController::listAction -> genus/list.html.twig), but even when I follow that, I don't think I *always* get auto-completion.

    So, you're not missing something - but I am surprised it doesn't work at all for you without this!

  • 2016-04-18 Hans Nieuwenhuis

    The auto-completion of the variables in the twigfile in PhpStorm only works for me if I add the next line at the top of the twig file

    {# @controller AppBundle:Genus:list #}

    Or have I missed something ?