- Mastering Symfony
- Sohail Salehi
- 1490字
- 2021-07-16 11:28:59
Creating templates with TWIG
Symfony has its own template engine called TWIG. It is a simple scripting language with a few tags and only three main rules:
- Whatever goes between {% %} should be executed
- Whatever is expressed via {{ }} should be printed
- Whatever is enclosed by {# #} is just a comment
As we continue, we will see how to use TWIG to create sophisticated and dynamic templates based on our project needs. For now, let's just see what a TWIG file looks like.
The render()
method from the previous topic has two parameters: the path to our TWIG template and its parameter. By default, all templates are in the app/Resources/views
folder. If you go there, you will find another folder called default
. That's why the middle part of the path parameter is default
:
return $this->render('default/index.html.twig', [ 'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'), ]);
Obviously, in the default
folder, we have the template file itself. So basically, we follow the [subdirectory in /Resources/views]/[template name]
format to access our templates.
There are two questions here:
- Why didn't we mention the full path as
Resources/views/Default
?By default, Symfony knows that all templates should be organized in
Resources/views
, so we can ignore that part and keep references nice and short. - Why do we even need a subfolder in
Resources/views
? Wouldn't it be cleaner and shorter if we keep every template in the root ofResources/views
?Yes, you can, but it is not very well organized. For example, imagine that we have several routes for different menu items:
/about
,/about/{name}
and/project
,/project/{id}
. You can keep templates for these routes in the root and give them unique names, or you can create subfoldersAbout/
andProject/
and keep the related templates in each of them.
Controller/View interaction
Let's add a new controller action in AppBundle
and call it aboutAction()
. This method will receive a name and says something about it:
// mava/src/AppBundle/Controller/DefaultController.php class DefaultController extends Controller { // ... /** * @Route("/about/{name}", name="aboutpage") */ public function aboutAction($name) { return $this->render('about/index.html.twig', array('name' => $name)); } }
The new @Route()
annotation for this method suggests that we need a new folder called about/
and an index template as follows:
{# mava/app/Resources/views/About/index.html.twig #} Hello {{ name|capitalize }}! <br/> Today is: {{"now"|date("m/d/Y")}}
As you can see, we can decorate the contents of a .twig
file in any way you like. For example, we can capitalize the name using the capitalize
filter and show the date by applying the date()
filter to the current timestamp.
User is one of the key entities of our project. They will be recognized as team members and organized in several different groups. Keeping this in mind, let's create an about page and see how we can see details about a specific user.
Conditional templates
In the previous example, imagine that we want to make the {name}
parameters optional. In other words, if there is a name in the URL, then we want to see a name-related message, and if there is no name, then we want to see a general message.
Let's start by changing the @Route()
annotation:
// mava/src/AppBundle/Controller/DefaultController.php class DefaultController extends Controller { // . . . /** * @Route("/about/{name}", name="aboutpage", defaults={"name":null}) */ public function aboutAction($name) { return $this->render('about/index.html.twig', array('name' => $name)); } }
The defaults
parameter nominates a default value for the name
variable. If we don't set a value for name, then it will be set to null. So now our aboutAction()
can receive requests from both /about
and about/{name}
.
Let's see how the template can handle these requests. Get rid of the previous contents of About/index.html.twig
and replace them with the following blocks:
{# mava/app/Resources/views/About/index.html.twig #} {% if name %} {{name}} is a member of our team. {% else %} mava is a web app for task management and team collaboration. <br/> {% endif %}
As you noticed, I used the {% if <condition> %}
tag to create a conditional structure. The idea is to create one template to handle various routes. Sure, we could create two separate templates and routes for /about
and /about/name
, but that's how we can work smarter and not harder. So basically, our template says that if I see a value for the name
variable, I will go in the if
block; otherwise, I will follow the else
block.
Make it dynamic
So far, it was about a static controller dealing with a static template. Let's see how we can feed our template with data from a database. Instead of handling database queries directly, we will use an Object Relational Mapper (ORM).
The Doctrine's job is to treat PHP classes and objects like they are tables and records. This way, we don't need to write SQL queries for Create, Read, Update, and Delete (CRUD) actions. All we need to do is ask our ORM to do the job for us. That makes coding a lot easier and fun.
Database configurations
Before using Doctrine, we need to make sure that our database settings are correct. Make sure that you have installed MySQL and its PHP drivers already and you have a valid MySQL username and password. You might find database management applications handy. I use MySQL Workbench, but feel free to choose anything that appeals to you.
To check the database configuration in your Symfony project, open the app/config/parameters.yml
file and set your own db username and password:
# app/config/parameters.yml parameters: database_driver: pdo_mysql database_host: 127.0.0.1 database_port: null database_name: mava database_user: <Your Username> database_password: <Your Password> mailer_transport: smtp mailer_host: 127.0.0.1 mailer_user: null mailer_password: null locale: en secret: ThisTokenIsNotSoSecretChangeIt debug_toolbar: true debug_redirects: false use_assetic_controller: true
As you can see, the database name for our project is mava
. To create this database, run the following command:
$ bin/console doctrine:database:create
Generating an entity
The database is created and we can create our tables in it. In Doctrine terminology, we don't call them tables anymore. Technically, they are PHP classes called entities. To generate an entity named User
, run the following command:
$ bin/console doctrine:generate:entity
Then, follow the interactive steps as follows:
The Entity shortcut name: AppBundle:User Determine the format to use for the mapping information. Configuration format (yml, xml, php, or annotation) [annotation]:
We only need three fields for our entity:
New field name (press <return> to stop adding fields): name Field type [string]: Field length [255]: Is nullable [false]: Unique [false]: New field name (press <return> to stop adding fields): bio Field type [string]: text Is nullable [false]: Unique [false]: New field name (press <return> to stop adding fields): email Field type [string]: Field length [255]: Is nullable [false]: Unique [false]:
If you check your database, you won't see the new table yet but there are some changes in our bundle directory.
There is a new Entity/
folder in our bundle and a PHP class called User.php
in it. This file contains some property definitions and getter and setter methods for each property:
/** * @var string * @ORM\Column(name="name", type="string", length=255) */ private $name; /** * Set name * @param string $name * @return User */ public function setName($name) { $this->name = $name; return $this; } /** * Get name * @return string */ public function getName() { return $this->name; }
The comments before the variable and method definition are not just ordinary comments. They are generated by console (when we choose an annotation) and a way of communication between our entity and Doctrine. For example, take a look at this comment:
/** * @var string * @ORM\Column(name="name", type="string", length=255) */
It tells Doctrine that we need a column called name
with a string
(255
) type. Now that we have our entity defined, it is time to generate the related table in our database:
$ bin/console doctrine:schema:update --force
Check your database now and you will see the User
table over there. For more details about the Doctrine annotation, visit http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html.
Installing bundles created by others
To play with our new entity, we need some records. We can add records manually or we can ask Symfony's console to do the job for us. These sample records are called data fixtures and there is a bundle to load and use fixtures. This bundle is called doctrine-fixtures-bundle
and this is how we install it:
- In the root of your project open
composer.json
file and add the following entry to it:"require": { //... "doctrine/doctrine-fixtures-bundle": "2.3.0" },
- Now add this bundle to your
vendor/
folder:$ composer update doctrine/doctrine-fixtures-bundle
- Finally, open the
app/AppKernel.php
file and add the following line at the end of the$bundles
array:// app/AppKernel.php //... $bundles = array( //... new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), ); //...
Congratulations, you just installed a new bundle in your project! To load data fixtures in our entity, we need to create them first.
- Learning C# by Developing Games with Unity 2020
- Learning Spring 5.0
- 造個小程序:與微信一起干件正經事兒
- PostgreSQL技術內幕:事務處理深度探索
- Hands-On JavaScript High Performance
- Apache Karaf Cookbook
- 軟件測試技術指南
- Learning Vaadin 7(Second Edition)
- Java EE核心技術與應用
- HTML 5與CSS 3權威指南(第3版·上冊)
- OpenMP核心技術指南
- Python程序設計開發寶典
- PhoneGap 4 Mobile Application Development Cookbook
- Java高手是怎樣煉成的:原理、方法與實踐
- HTML5與CSS3權威指南