官术网_书友最值得收藏!

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 of Resources/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 subfolders About/ and Project/ 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.

Note

There is a lot to say about TWIG and I will show you how to use it practically in the chapters to come.

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).

Note

Doctrine is the ORM that we are using in this book. It is powerful and by default integrated into Symfony, which makes it very convenient to use.

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:

  1. 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"
    },
  2. Now add this bundle to your vendor/ folder:
    $ composer update doctrine/doctrine-fixtures-bundle 
    
  3. 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.

主站蜘蛛池模板: 多伦县| 长治县| 苍溪县| 奉新县| 洛宁县| 龙川县| 南昌县| 江山市| 通海县| 江孜县| 榕江县| 沙洋县| 平南县| 罗平县| 兰坪| 察雅县| 台北市| 阿城市| 沽源县| 临潭县| 博白县| 武义县| 肃南| 新宁县| 东海县| 兴和县| 类乌齐县| 托里县| 长沙市| 宁晋县| 苏尼特右旗| 武城县| 保德县| 宜宾市| 阿拉善右旗| 宣汉县| 拉孜县| 凉山| 阜宁县| 偃师市| 师宗县|