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!

  • 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 ?