If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Let's finally make this page real with a template. Return
$this->render('genus/list.html.twig')
and pass it a genuses
variable:
... 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:
{% 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:
... 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:
... lines 1 - 10 | |
<tbody> | |
{% for genus in genuses %} | |
<tr> | |
<td>{{ genus.name }}</td> | |
<td>{{ genus.speciesCount }}</td> | |
</tr> | |
{% endfor %} | |
</tbody> | |
... lines 19 - 21 |
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?
... 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
:
... 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":
... 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:
... 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:
... 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.
"Houston: no signs of life"
Start the conversation!
// 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
"knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
"doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0", // v3.1.3
"nelmio/alice": "^2.1", // 2.1.4
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}